본문 바로가기
우아한테크코스/Level1

Level1 미션 + 학습 로그 정리

by 조엘 2021. 4. 26.

안녕하세요! 조엘입니다! 🤞🤞

 

우아한테크코스에서는 미션을 PR로 제출하고, 해당 미션에서 배운 것을 학습로그로 기록하고 있어요.

레벨 1의 학습 목표는 다음과 같았는데요.

 

  • 자바 프로그래밍 언어에 대한 기본 문법을 익혀 프로그래밍하는 경험을 한다.
  • 읽기 좋은 코드를 구현하는 것이 왜 중요한지와 코드를 개선해 읽기 좋은 코드로 변경해 보는 경험을 한다.
  • 자신이 구현한 코드에 대해 단위 테스트리팩토링하는 경험을 한다.
  • 웹 프론트엔드에서 웹 백엔드까지 프로그래밍해 웹 애플리케이션을 개발하는 경험을 한다.

 

학습로그를 정리하면서 레벨 1에서 배워야 할 부분 중 단위 테스트에 대한 공부가 조금 모자랐다는 생각이 드네요!

물론 테스트 코드를 작성하고 TDD 기반으로 미션을 진행하려고 노력은 했습니다만,,,

단위 테스트의 장점에 대해 설명해보라고 하면 횡설수설 할 것 같아요 🤔

 

레벨 2에서는 Spring을 배우는데요.

Spring에서 테스트 코드를 작성해 나가면서, 테스트에 어떠한 장점이 있는지 더 느껴보도록 하겠습니다. 💪

 

[미션 정리]

 

미션명 PR 링크
자동차 경주 <1단계> github.com/woowacourse/java-racingcar/pull/183
자동차 경주 <2단계> github.com/woowacourse/java-racingcar/pull/234
로또 <1단계> github.com/woowacourse/java-lotto/pull/271
로또 <2단계> github.com/woowacourse/java-lotto/pull/331
블랙잭 <1단계> github.com/woowacourse/java-blackjack/pull/146
블랙잭 <2단계> github.com/woowacourse/java-blackjack/pull/193
체스 <1,2,3단계> github.com/woowacourse/java-chess/pull/172
체스 <4,5단계> github.com/woowacourse/java-chess/pull/236

 

[학습 로그 정리]

 

[OOP] 일급 컬렉션 - 3

내용

  • 비즈니스에 종속적인 자료구조 생성
  • 불변성을 보장함으로써 부작용 최소화
  • 상태와 행위를 한 곳에 관리하도록 함
  • 이름이 있는 컬렉션으로, 의미 부여 가능

링크

 

[OOP] 정적 팩토리 메서드 - 3

내용

  • 객체 생성의 역할을 하는 클래스 메서드
  • 이름이 있어, 가독성이 좋아짐
  • 호출할 때마다 새로운 객체 생성하지 않아도 됨
  • 객체 생성을 캡슐화 할 수 있음

링크

 

[OOP] DTO - 2

내용

  • 계층 간 통신 용도로 오가는, 데이터 교환을 위한 객체
  • 로직이 없는 순수한 데이터 객체로, getter와 setter만을 가짐

링크

 

[JDK] BigDecimal - 2

내용

  • 부동 소수점 연산을 정확하게 수행하도록 지원
  • 돈을 다룬다면 필수로 써야함

링크

 

[JDK] Stream API - 3

내용

  • Stream API를 활용한 데이터 처리

링크

 

[Design Pattern] 전략 패턴 - 2

내용

  • 랜덤한 값을 테스트하기 위해 인터페이스로 분리, 테스트 가능한 구조로 변경
  • 구조
    • Context: 갖고 있는 전략 콘크리트에 따라 행동이 달라짐
    • Strategy: 제공하는 알고리즘에 대한 공통의 연산들을 interface로 정의
    • Strategy Concrete Class: 실제 알고리즘
  • 장점
    • 상속을 사용하지 않음
    • if문의 제거를 가능하게 한다
    • 구현의 선택이 가능해진다
  • 단점
    • 객체 수 증가
    • 서로 다른 전략을 이해해야 함

링크

 

[OOP] 응집도와 결합도 - 2

내용

  • 응집도: 모듈 내부의 기능적인 집중 정도
  • 결합도: 모듈과 모듈간의 상호 의존 정도
  • 독립적인 모듈이 되기 위해서는 응집도는 강하고, 결합도는 약해야 한다

링크

 

[JDK] Stream.forEach vs Collection.forEach - 2

내용

  • Stream.forEach
    • Stream 객체를 생성한 후에 forEach 메서드 호출
      • 따라서 중간 연산에 해당하는 로직과 함께 써야 유의미하게 Stream을 사용한 것
    • 반복 도중에 다른 쓰레드에 의해 수정될 수 있다
    • parallelStream을 통해 여러 쓰레드에서 스트림을 실행할 수 있다
    • 무조건 요소의 끝까지 반복을 돌게 된다.
  • Collection.forEach
    • 따로 객체를 생성하지 않고 forEach 메서드 호출
      • forEach 메서드는 Iterable 인터페이스의 default 메서드
      • Collection 인터페이스에서 Iterable 인터페이스를 상속하기에 바로 사용한다.
        • 인터페이스는 인터페이스를 상속 받을 수 있다 (다중 상속도 가능하다!)
    • 멀티쓰레드 환경에서 락이 걸려있기에 더 안전하게 사용할 수 있다

링크

 

[OOP] Enum 활용 - 1

내용

  • 정의
    • 관련한 상수 Singleton 객체의 모음집
    • 컴파일 당시, 가능한 모든 값을 안다면 Enum 활용을 고려할 것
  • 장점
    • Java의 Enum은 상태와 행위를 동시에 관리할 수 있다
    • 컴파일 시에 정의한 타입 이외의 타입에 대해 오류를 발생시킨다

링크

 

[JDK] new ArrayList<>() vs Arrays.asList() - 2

내용

  • new ArrayList<>()
    • java.util.ArrayList 소속
    • List에 대한 모든 operation을 쓸 수 있음
    • new로 생성해주기에, 넘겨받은 원본 리스트에 대한 참조는 끊는다
      • 여기서 참조를 끊는다는 것은, 새로운 주소에 List를 만들고, 넘겨받은 List들의 원소를 하나하나 복사해주어 만들어 반환한다는 것
      • 넘겨준 원본 List에 add, remove등의 연산을 해도 새롭게 만든 ArrayList에는 영향이 가지 않는다
      • 하지만 List안에 들어있는 객체들은 그대로 얕은 복사가 되기 때문에 넘겨준 원본 List에서 원소로 가지고 있는 객체의 필드값을 조작하면, 새롭게 만든 ArrayList 내의 원소로 포함된 객체에도 영향을 미친다.
  • Arrays.asList()
    • java.util .Arrays.ArrayList 소속
    • 읽기만 가능한 List
    • add(), remove() 메서드를 통해 해당 list에 원소를 추가/삭제하려고 하면...
      • "UnsupportedOperationException"을 반환
    • 원본 배열의 참조도 끊지 않는다. 즉 얕은 복사로 주소를 공유한다.
    • List 형태로 배열을 포장한 것이라고 보면 됨

링크

 

[JDK] HashMap vs LinkedHashMap - 3

내용

  • HashMap
    • 중복된 원소를 허용하지 않으며, 순서도 고려되지 않음
    • HashMap은 key와 value를 저장하는 두개의 배열로 구성되어 있음
    • Key값이 어디에 저장될지는 다음과 같이 정의됨
      • Key값으로 저장할 Object의 hashCode()를 구한다
      • 정수로 반환된 hashCode() 값을 버킷의 수로 나눈 나머지를 구한다 (초기 버킷 수는 16개)
      • 그렇게 구한 값이 배열의 index가 된다
      • 해당 index에 Object를 저장한다
  • LinkedHashMap
    • HashMap과 동일하게 데이터를 저장
    • 하지만 Doubly-Linked-List를 통해 데이터 저장 순서를 동시에 기록함
    • 따라서, 데이터가 저장된 순서대로 접근할 수 있음

링크

 

[JDK] 함수형 인터페이스 - 3

내용

  • 람다식
    • 메서드를 하나의 "식"으로 표현한 것
      • 따라서 메서드의 역할을 람다식으로 대체할 수 있음
      • 이는 일급객체로, 변수처럼 다루게 되었음
    • Java의 모든 메서드는 클래스/인터페이스 내에 포함되어 있어야 호출할 수 있다
      • 람다식의 소속 클래스는 익명 클래스이다
        • 익명 클래스: 클래스의 선언과 객체 생성을 동시하는, 한 번만 사용될 수 있고 오직 하나의 객체만을 생성할 수 있는 일회용 클래스
    • 하나의 메서드가 선언된 인터페이스를 정의하여, 람다식을 다루게 된다
      • @FunctionalInterface 어노테이션 추가하여 정의
      • 하나 이상의 메서드를 인터페이스에 추가하려는 경우, 컴파일 에러 발생
      • 결국 해당 인터페이스를 매개변수로 넘겨주고, 인터페이스 내부에 있는 하나의 메서드를 실행하는 방식으로 사용
  • java.uti.function 패키지
    • 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 정의해놓음
    • 재사용성/유지보수를 위해 가능한 이 패키지의 함수형 인터페이스를 활용하자
    • https://github.com/woowacourse/java-blackjack/pull/193/commits/d472685930c7ccd0a9f7ec03a918de237e6549b1
    • 해당 커밋에서 사용한 BiFunction<Integer, Integer, Boolean> 함수형 인터페이스는...
      • 두 개의 Integer 형식의 매개변수를 받아, Boolean 타입의 결과를 반환하는 함수
    • 나머지 사용법은 아래 링크 참조

링크

 

[OOP] VO - 3

내용

  • 정의
    • 도메인에서 한 개 혹은 그 이상의 속성들을 묶어서 "특정 값"을 나태내는 객체
      • ex) 좌표, 금액, 날짜 등
  • 특징
    • equals, hashCode 메서드를 재정의 할 것
      • 값 자체를 나타내는 객체이기에, 내부의 속성과 타입이 같다면 같은 VO로 보는 것이 타당하다
    • 불변객체로 만들 것
      • VO는 속성값 자체가 해당 객체의 고유한 식별값으로, 내부 속성값을 바꿀 수 있게 하면 안 된다

링크

 

[Design Pattern] 상태패턴 - 3

내용

  • 정의
    • 기능이 "상태"에 따라 다르게 동작해야 할 때 사용하는 패턴으로, 가능한 "상태"들을 Class로 정의해 놓은 것!
      • "상태" 객체가 필요한 "기능"을 제공하게 된다.
  • 구현
    • 상태 사용자(context): 클라이언트로 부터 기능 실행 요청을 받으면, 상태 객체에게 해당 기능 처리를 위임한다
    • 상태(state): 구체화된 여러 상태들의 기능들을 추상화함으로써, 변화하는 부분을 담당한다
      • 상태 콘크리트(Concrete state): 실제 상태의 기능들을 구현
  • 장점
    • 상태에 따른 if - else 분기를 안 만들어도 된다.
      • 유지보수가 쉬워지고, 단일 책임 원칙을 준수하기도 수월해진다.
    • 상태별로 동작 수정이 쉬워짐
      • 상태에 관련된 코드가 한 곳에 모여있어 안전하고 빠른 구현이 가능하다

링크

 

[JDK] hashCode & equals 의 동치성 - 3

내용

  • HashSet/HashMap 같은 경우, hashCode와 equals 가 같다면, 같은 Key값으로 인지
  • 2개의 "같아야 하는"객체를 HashMap에 넣었는데, hashCode/equals가 다르게 정의되어 있다면,
    • 즉, hashCode는 같고 equals는 다르다면
    • 2개의 객체는 각각 다른 Key값으로 HashMap/HashSet에 저장된다.
  • 해당 방식은 결국 고유한 Key값을 통한 Value의 접근을 방해하는 Side Effect를 발생시킨다.
  • 따라서 객체의 고유함을 보장할 수 있도록, hashCode와 equals가 같이 동작하도록 정의해야 한다.

링크

 

[OOP] 객체 생성의 캡슐화 - 2

내용

  • 체스에 필요한 팀 객체를 인스턴스화 하기 위해, Team 클래스의 생성자를 통해 각 팀을 생성해주었다.
  • 하지만 생각해보니까, 체스에는 블랙팀, 화이트팀만 있으면 된다.
  • 그러면 굳이 생성자를 열어두지 말고(물론 열어줘도 됨), BlackTeam을 만드는 정적 팩토리 메서드와 WhiteTeam을 만드는 정적 팩토리 메서드를 만들어 보는 것은 어떨까?
  • 해당 방식으로 코드를 리팩토링 했더니, Team 생성에 필요한 정보를 생성자를 통해 외부로 공개하지 않고, 체스에 필요한 팀을 만드는 역할을 충분히 하게 되었다.

링크

 

[OOP] SOLID 원칙 중 LSP - 2

내용

  • 리스코프 치환 원칙 정의
    • 상위 타입의 객체를 하위 타입의 객체로 치환해도, 상위 타입을 사용하는 프로그램은 정상적으로 동작해야한다
    • OCP를 뒷받침 하는 "다형성"에 관한 원칙
  • LSP 위반 신호
    • isInstanceOf는 전형적인 LSP 위반 신호
      • 상위 타입을 그대로 쓰지 못하고, 하위 타입 인스턴스 인지 검사하는 것 자체가, 상위 타입으로 프로그램을 정상 작동 못시킨다는 것!
    • 이를 해결하려면, 더 "추상화" 시켜볼 것
      • "구체적 구현에 따라 변화하는 기능"으로 만들어 일을 처리해보자...!

링크

 

[Architecture] Service Layer

내용

  • Layer가 필요한 이유
    • Layer를 통해 역할을 추상화 할 수 있음
    • 다른 계층 끼리 부품을 갈아끼우듯 변환 가능
    • 컴포넌트들의 의존 계층 관계 유지 가능
  • Service Layer
    • 어플리케이션의 비즈니스 로직을 캡슐화하고, transaction을 조절하는 역할

링크

 

[JS] fetch API

내용

  • Ajax
    • 비동기 방식으로 데이터를 주고받는 JS 기술
    • 페이지 새로 고침 없이 서버에 요청하고, 응답을 받을 수 있음
  • fetch
    • Ajax 기술의 복잡도를 낮춰 사용할 수 있도록 fetch 보급
    • Promise를 반환함
      • 따라서, then() 메서드에 응답 처리하는 콜백함수 추가할 것

링크

 

[JDBC] 자원 반환

내용

  • Connection, Statement/PreparedStatement, ResultSet 들을 모두 close() 해줘야 한다
  • 그렇지 않다면...
    • Statement가 제대로 안 닫히면, 생성된 Statement의 갯수가 증가하여, 더 이상 Statement를 생성할 수 없게 됨
    • close() 하지 않으므로, 불필요한 자원을 낭비하게 됨 (네트워크/메모리)
    • 커넥션 풀을 사용하지 않는 상황에서 Connection 안 닫아주면 DBMS에 새로운 Connection을 만들 수 없음
  • 해결 방법으로 커넥션을 관리하는 class를 생성하거나, try-with-resource를 사용할 수 있다

링크

 

[Java] 멀티 스레드 환경에서 고려할 것

내용

  • 만약 인스턴스 변수가 있는 클래스에서 멀티 스레드 환경을 지원한다면, 해당 인스턴스 변수에 대해 쓰레드 동기화를 확보해야함
    • synchronized 예약어로 필요한 영역을 임계 영역으로 지정할 수 있음
    • 이렇게 된다면, 하나의 쓰레드만 접근/처리 가능하여 성능이 떨어질 것임
  • 만일 클래스를 stateless하게 만든다면, race-condition이 일어날 인스턴스 변수가 없다
    • 멀티 스레드 환경을 쉽게 지원할 수 있음

링크

 

+) 공부에 도움 주신 선배 개발자 분들 감사합니다!

반응형

댓글1