들어가며

안녕하세요! 드디어 씁니다.. 우테코 레벨1 회고~ 더 까먹기 전에 후딱 써버리겠습니다.

백엔드 Level 1 미션은 Java를 이용해 프로그램을 완성하는 미션이었습니다.

여러 제약사항이 주어지며, 첫 pr은 페어 프로그래밍으로 완성해 제출해야합니다.

1. 백엔드 Level 1 미션 - 블랙잭

1. DTO 생성을 어디에서 하는게 좋을까요?

Converter라는 중간 계층을 두면 여러 명이 협업할 때, 컨벤션을 맞춘다 하더라도 비지니스로직이 들어가는 경우가 있습니다. 따라서 DTO가 이동하기 전 계층에서 DTO를 만드는게 더 편리합니다.

2. extends와 implements의 적절한 사용 방법은?

클래스를 상속받는건 부모와 자식이 강결합되는데, 추가 요구사항이 생겼을때 수정이 적은 방법은 무엇인가?를 고민해보면 좋습니다. is-a 관계를 유지하면서 중복 코드를 줄일 수 있다면 extends, 그렇지 않다면 implements를 사용하면 좋습니다.

3. 테스트를 위한 메서드를 만들어도 괜찮을까?

테스트를 위한 메서드를 만든다면, 테스트를 위한 메서드의 테스트가 존재해야할 수 도 있다. 이런 경계를 확실히 하기 어렵기 때문에 테스트를 위한 메서드가 필요한 경우가 생긴다면, 코드를 리팩토링하는게 더 좋은 방향이다.

4. final 키워드는 왜 필요하고 클래스, 메서드, 필드에 붙을 때 각각 어떻게 다를까?

final 키워드는 불변성의 의미를 가집니다. 필드에서 필드 값의 변경 금지를 강제하기 위해서 필요합니다. 메서드에서는 오버라이딩 금지를 강제하기 위해 필요합니다. 클래스에서는 상속 금지를 강제하기 위해 필요합니다.

5. 테스트 코드의 역할과 정책이 수정되었을 때 테스트 코드를 수정하는게 옳은 방향

테스트는 단순히 코드가 동작하는지 확인하는 것을 넘어서 1) 정책이 프로덕션 코드에 올바르게 반영되어 있는지 검증하고, 2)해당 정책을 다른 개발자들도 이해할 수 있도록 문서 역할을하며 3)이후 달느 개발자가 코드를 수정할 때 의도와 다르게 변경되는 것을 방지하는 역할을 합니다.

따라서 정책이 변경되었다면, 그 정책을 검증하고 있던 테스트 역시 함께 수정 되는 것이 자연스러운 흐름입니다.

6. ENUM의 장점은?

enum의 장점은 3개가 있습니다.

  1. 상태나 메세지의 가독성이 좋아집니다.
  2. 상태나 메세지를 공통으로 관리하기 때문에, 변경에 유리해집니다.
  3. 각 상태에 맞는 행동이나 값을 정의할 수 있습니다.

7. null을 반환하는게 좋을까?

많은 사람들이 null은 무조건 나쁜 것이고 없애야 하는 것이라고 생각합니다. 하지만 null을 사용하는게 나쁜게 아니라, null을 옳지 않은 방향과 목적으로 사용하는게 나쁜 것입니다. The Philosophy of Null에서 좋은 예시를 들어주었습니다. 사망하지 않은 사람의 사망일을 저장하는 필드에 기본값을 넣는다면 그 값은 논리적으로 더 어색하기 때문에 이 경우에는 null을 사용하는게 더 올바르다고 합니다. 따라서 null 사용을 무작정 제한하기 보다는 기본값을 반환하는게 더 어색한 경우에 null을 사용하는게 좋은 방향입니다.

8. 방어적 복사란 무엇일까?

방어적 복사는 생성자의 인자로 받은 객체의 복사본을 만들어 내부 필드를 초기화 하거나, getter 메서드에서 내부의 객체를 반환할 때, 객체의 복사본을 만들어 반환하는 것을 의미합니다. 만약 방어적 복사를 하지 않고 반환하면, 클래스 내부의 객체 필드가 의도하지 않은 변경이 일어날 수 있습니다.

이때 중요한 점은, 방어적 복사는 깊은 복사가 아니라 얕은 복사입니다. 따라서 객체 내부의 필드는 수정이 가능할 수 있기 때문에 이 점 또한 유의해야합니다.

추가로 방어적 복사와 Unmodifiable Collection 이 글을 참고하시면 더 도움이 될 것 같습니다.

9. 더 구체적인 메서드명을 만들자.

리뷰어와 코치의 공통 피드백에서는 메서드명을 도메인과 관련된 용어를 사용하고, 의도와 작동 방향을 정확히 알 수 있도록 하라고 하셨습니다.

블랙잭 미션에서는 사용자가 뽑은 카드를 Hand라고 하고, 카드를 뽑는다는 뜻은 Hit, 뽑지 않는 것은 Stand를 사용합니다. 저는 카드를 뽑는 메서드의 명을 addCard로 하여 도메인과 관련된 용어를 사용하는 것이 오해하지 않을 수 있다고 이야기해 주셨습니다.

아래의 코드는 의도와 작동 방향에 대한 피드백을 받은 코드입니다.

public boolean drawDealerCard() {
    if (dealer.calculateScore() <= PolicyConstant.DEALER_HIT_MAX_SCORE) {
        dealer.addCard(List.of(cardMachine.drawCard()));
        return true;
    }
    return false;
}

drawDealerCard()라는 메서드명만 보면, 카드를 뽑는 행위만 있지만, 코드에서는 뽑는것 뿐 만 아니라 카드를 더 뽑을 수 있는지 없는지도 반환하고 있습니다. 이 경우, 다른 개발자가 drawDealerCard()의 의미를 파악하기 위해 메서드를 또 한 번 들여다 봐야하는 번거로움이 생길 수 있다고 이야기 해주셨습니다.

2. 백엔드 Level 1 미션 - 장기

1. 예외 메세지를 왜 남기는 걸까?

예외 메세지는 예외 상황을 표시하고 적절한 처리를 하기 위함입니다. 그렇다면 해당 예외 상황이 왜 발생했는지를 잘 드러내야합니다. 또 더 나아가서 실제 운영환경에서 버그가 발생하여 장애대응을 한다고 생각해보면 결국 디버깅의 시작점이 이 예외메시지일 것입니다. 나중에 제가 이 예외 메세지를 보고 디버깅을 한다고 생각하면 중요한 과정일 것이라고 리뷰어께서 이야기 해주셨습니다.

2. getter를 사용하지 않고 객체에 메세지를 보내야 하는 이유

관련 글에서는 아래와 같이 이야기 하고 있습니다.

모든 멤버변수에 getter를 생성해 놓고 상태값을 꺼내 그 값으로 객체 외부에서 로직을 수행한다면,

객체가 로직(행동)을 갖고 있는 형태가 아니고 메시지를 주고 받는 형태도 아니게 된다.

또한, 객체 스스로 상태값을 변경하는 것이 아니고, 외부에서 상태값을 변경할 수 있는 위험성도 생길 수 있다.

따라서, getter를 사용하기 보단, 객체에 그 행동을 요청을 하도록 메세지를 보내는 것이 더 적절하다는 것을 알았습니다.

<!--### 3. instanceof를 지양하자 https://tecoble.techcourse.co.kr/post/2021-04-26-instanceof/-->

3. 왜 Repository에서 정렬 하는 것이 더 좋을까?

저는 Repository에서 정렬을 하는 것도 비지니스 규칙이라고 생각하여 리뷰어에게 어떤 계층에서 정렬을 하는게 좋을지 여쭈어 보았습니다.

질문

다만 이렇게 Repository에서 정렬까지 담당하게 되면, 비즈니스 규칙이 데이터 접근 계층으로 내려가는 것처럼 느껴지기도 해서 이 부분이 조금 헷갈렸습니다.

그래서 findAll()로 먼저 조회한 뒤 서비스 레이어에서 정렬하는 것이 더 맞는지,

아니면 Repository에서 ORDER BY를 통해 정렬된 결과를 반환하는 것이 더 적절한지 로키의 의견이 궁금합니다!

답변

말씀하신 것처럼 정렬을 어느 계층의 역할이냐?에 대해서 충분히 고민해볼 수 있는 포인트라고 생각해요. (생략)

예를 들어 유명한 SNS 서비스 (ex. 인스타, 페이스북, 트위터 etc)에서 findAll을 호출한 뒤, Serivce단에서 정렬을 하게된다면 어떻게 될까요?

예상하셨을지 모르겠지만, 조회한 데이터들을 객체로 가지고오는 도중에 OOM이 발생하게될거에요.

그렇기 때문에 이런 경우에는 데이터베이스 단에서 정렬을 시킨 뒤, OOM이 발생하지 않을정도의 적절한 사이즈로 데이터를 조회하여 객체로 변환해줘야할거에요.

말씀하신 기준이 가능해지려면 갯수가 적다는 기준(추후에도 갯수가 많아지지 않는다는 전제도 포함)에서 Service 단에서의 정렬로 충분히 고려해볼 수 있다고 생각해요. (생략)

리뷰어가 이야기 해주신 것 처럼 정렬도 비지니스 로직은 맞지만, 조회한 데이터들을 가져올 때, OOM이 발생할 수 있기 때문에 데이터베이스에서 정렬을 한 뒤, 적절한 사이즈로 데이터를 조회해야한다는 것을 알았습니다.

5. 설계 비율은 얼마나 차지하는게 좋을까?

장기 미션에서 저는 많은 시간을 설계를 하며 보냈습니다. 그렇다보니 리뷰가 늦어지는 일이 꽤 자주 있었습니다.. 그래서 이와 관련하여 리뷰어에게 여쭈어 보았습니다:)

답변

먼저 설계를 과하게하면할 수록 그 설계대로 진행이 안될 확률이 높다는 점을 먼저 말씀드립니다.

왜냐하면 앞부분이 조금이라도 달라지기시작하면 뒷부분은 더 크게 달라지기 시작하거든요. 😢

그렇기 때문에 설계를 오래 한다고 항상 더 좋은 결과가 나오는 건 아니에요.

특히 미션에서는 처음부터 완벽하게 설계하려고 하기보다, 핵심 규칙이 자연스럽게 들어갈 정도만 설계하고 바로 테스트와 구현으로 검증해보는 쪽이 더 효율적일 때가 많을거에요.

설계에 너무 오래 머무르는 느낌이 들면, 고민 중인 포인트를 테스트 케이스 하나로 옮겨서 가장 단순한 구조로 먼저 구현해보세요.

보통은 그 다음 리팩터링에서 더 좋은 설계가 보이기 시작할거에요. (생략) 조금 더 액션 플랜으로 제시를해드려보자면...!

구현은 최소한으로 딱 정해놓은 시간 (ex. 10~20분) 정도만 소비하고 이후에는 구현하면서 필요한시점에 하나씩 살을 덧붙이는 방향으로 나아가보면 좋을 것 같아요.

그리고 추가로 슬랙 디엠으로 설계와 관련된 글도 받았습니다.

피드백을 받은 것 처럼 앞으로는 설계에 지나친 시간을 투자하지 않고, 현실적으로 주어진 시간만 투자하여 개발을 해보려고 합니다. 소스 코드가 곧 설계다!

6. 네이밍 잘 하자

리뷰 과정에서 메서드명으로 인해 작은 소통 오류가 있었습니다. 만들때는 이해하기 쉬울 것 같은 메서드명을 만들기 위해 그 당시에는 노력했었는데, 저도 나중에 다시 보니까 무슨 메서드였는지 기억나지 않아 한참 코드를 들여다 보았었습니다. 그래서 협업과 저 스스로도 코드를 빠르게 돌아볼 수 있도록 네이밍을 더 잘해야겠단 생각이 들었습니다.🥲

3. 알고리즘 스터디

백준의 끝

백준이 4월 28일자로 서비스를 종료한다고 합니다.. 백준 기반으로 공부해왔었는데, 많이 혼란스럽습니다.

4. 사이드 프로젝트

구구와 원온원을 하면서 사이드 프로젝트를 해보라는 권유를 받았습니다.

원래는 미션에 올인하는 형식으로 가려고 했지만, 원온원 후에 미션과 사이드 프로젝트를 병행하기로 했습니다.

미션에서 배운 것을 사용해도 좋지만 최근 AI의 발전이 급속도로 이루어지고 있기 때문에, AI를 사용하여 노코드로 프로젝트를 진행해보기로 했습니다.

아래는 어느정도 만들어본 프로젝트 입니다ㅎㅎ 언젠가 소개해드리겠습니다.

5. 구리마을

백엔드는 사람 수가 많기 때문에 약 20명 내외로 나누어 각각을 하나의 마을로 묶여 생활합니다. 저는 담당 코치들의 이름을 따 구리마을(구구-리사 마을)에 살고? 있습니다.

미션관련해서 토론할때도 재미있었고, 놀러다닐 때도 재미있게 놀러다닌 것 같습니다 ㅋㅋ

마치며

마지막은 역시 구리마을 사람들 최고라는 말로~ 마무리 해야죠~ 레벨2에도 잘부탁드립니다🙇🏻‍♂️

레벨1때는 시간 관리를 하지 못해 회고를 거의 못썼는데, 레벨2 부터는 꼭.. 회고를 자주 써보려고합니다.

그럼 다들 담에 뵈어요~~