코드숨 총 정리 - 8주간의 기록

beomdrive·2022년 12월 12일
1

멘토링

목록 보기
7/8
post-thumbnail

8주간의 여정

10월부터 시작한 코드숨 스프링 과정이 막을 내렸다.
마지막 날 마지막 PR을 날리고, 퇴사 후 9월부터 쭉 공유 오피스에서 함께 공부하고 있는 스터디 멤버들과 맥주 한잔을 기울였다. 8주 동안 단 하루도 쉬지 않고 미션 과제를 수행하며, 매일 저녁 9시마다 늦지 않게 PR을 날리려고 고군분투했었던 기억이 생생히 났다.
공유오피스 멤버 중 코드숨을 수료했던, 나한테 코드숨을 추천해주었던 분이 “코드숨 모든 수료생 중에서 제일 열심히 하셨을 것 같다”라는 말에 바로 맥주 한캔을 원샷했다.

되돌아보면 정말 매일 하루하루 리뷰를 받으려는 마음이 집착급이었던 것 같아 멘토님들에게 죄송스러운 마음도 들었다. 멘토님들도 현업과 병행하시면서 바쁘실텐데, 하루라도 리뷰가 안달렸으면 아침에 바로 디스코드로 문의를 날렸었다.
지금 생각해보면 하루쯤은 멘토님들도 일이 있으셨을텐데, 내 조급함과 이전 회사에서부터 키워왔던 피드백에 대한 갈망이 멘토님들을 8주간 하루도 빠짐없이 귀찮게 만들었던 것 같다.

그래서 그런지 덕분에 코드숨 멘토링 과정 시작 전과 수료 이후의 내 모습은 객관적으로 봤을 때도 많은 것을 배웠고 성장했다고 당당하게 말할 수 있게 되었다.

이 글은 코드숨 과정이 끝나고 1주일이 지난 시점에 작성하는 회고이다. 원래 이 회고를 과정이 끝난 직후 바로 작성하려 했는데, 집을 이사하기도 했고 8주 동안 경주마처럼 달린 여파 때문인지 이틀 동안 컴퓨터를 키지 않고 방 안에서 쉬엄쉬엄 방 정리를 하며 뒹굴거렸다.
덕분에 머릿속이 환기가 되어 코드숨의 과정 전체가 새록새록 기억이 나며 정리되었다. 아래에서는 8주 동안 내가 어떤 개념들을 습득했는지, 과연 집착급으로 리뷰를 받았다던데 얼마나 성장했는지, 학습 내용들을 정리하며 되돌아보려고 한다.

코드숨에서 배운 것들을 1주차부터 8주차까지 모든 리뷰를 다시 읽어보면서 크게 8가지의 카테고리로 분류해보았다.

  • OOP & Clean Code
  • CS
  • Github
  • Java
  • Spring
  • Test
  • Architecture
  • 교양

카테고리의 각 내용들은 하나에 17개인 카테고리가 있고, 1개인 카테고리도 있다.
각 내용에는 관련 리뷰를 링크를 달아놓아서, 자주 리마인드 하며 질 좋은 코드 퀄리티를 유지하고 좋은 엔지니어의 마인드를 되새기려고 한다.

또한 블로그에 7주차와 8주차의 관한 회고는 업로드 되지 않았는데, 그 이유는 해당 주차에 대한 학습 내용을 노션에는 적어놨지만 관련 내용을 딥하게 공부하지 못해 아직 내 것으로 만들지 못했다.

7주차에서는 Spring Security와 인증&인가에 대한 학습을 했고, 8주차에서는 RestDocs를 통한 API 문서화 작업 및 Docker 에 관해 학습을 하였다. 하지만 관련 내용은 따로 추후에 학습하고 싶었고, 그 때 당시에는 Test와 아키텍처에 대해 집중하고 있었기 때문에 학습 내용에 관한 정리를 미루었다.
(아 물론 미션을 통해 구현은 해보았다. 하지만 구현해본 것과 제대로 학습한 것은 다르다고 생각한다.)

Security와 RestDocs, Docker에 관해서는 조만간 만들 토이 프로젝트에 적용할 때, 공부 내용을 따로 업로드해보겠다.

마지막으로 코드숨 과정에 대한 후기 작성을 끝으로 8주 간의 학습 내용 정리본을 소개하려고 한다.

요즘 코드 리뷰를 해주는 멘토링 과정이 많다. 백엔드는 정말 넘쳐난다. 하지만 내가 느꼈던 코드숨만의 장점은 코드 리뷰의 양이라고 생각한다. 과연 이렇게까지 코드 리뷰를 많이 해주는 멘토링 과정이 있을까? NextStep, F-Lab, 프로그래머스 등등 좋은 멘토링 과정은 꽤 많다. 하지만 내가 고민하고 작성한 코드에 대해 원 없이 질리도록 리뷰를 받고 싶다면 코드숨만한게 없다고 개인적으로 생각한다.
아래는 8주 간 코드숨에서 PR을 통해 토론을 했던 코멘트의 개수이다. (총 544개이다)

  • 1주차: 106개
  • 2주차: 100개
  • 3주차: 77개
  • 4주차: 59개
  • 5주차: 70개
  • 6주차: 57개
  • 7주차: 36개
  • 8주차: 39개

물론 코멘트만 많다고 좋은건 아니지만, 기술적인 토론과 질문을 이렇게까지 많이 할 수 있는 곳이 많지는 않을거란 생각이 든다. 정말 종립 멘토님, 영환 멘토님, 윤석 멘토님에게 감사한 마음과 더불어 너무 귀찮게 많이 질문한 것 같기도 해서 죄송한 마음도 든다 😂

나는 코드숨 과정을 통해 솔루션 SI를 다니면서 생각 없이 개발했었던 나쁜 습관들을 모두 물갈이 하게 된 계기였다고 생각한다. 개발자 인생에서의 터닝포인트가 되었던 것 같다.
이제 코드숨에서 습득한 학습 내용을 계속 리마인드하면서 앞으로는 정말 찐개발자 답게 프로그래밍을 할 것이다 :)

내가 해당 과정에서 제일 잘 배웠다고 생각하는 것은 다음과 같다

  • 개발 및 학습하는 방법 (공식 문서, 문제 해결 방식 등등)
  • 객체 지향 관점의 사고를 늘리게 된 계기 + 객체지향 프로그래밍
  • TDD

물론 위의 내용은 내가 관심있게 중점적으로 공부한 것들이고, 코드숨에서 제공하는 학습 내용은 더욱 많다.

  • Spring Boot
  • Test Code (BDD, Mock …)
  • JPA
  • Interceptor
  • Spring Security
  • RestDocs
  • Docker
  • 등등

이 글을 읽는 분들 중 코드숨을 고민하고 계신 분이 있다면, 주제 넘지만 이런 말씀을 드리고 싶다.

  • 인강, 수업 등 주입식으로만 학습하고, 막상 프로젝트에 적용해보려니까 막막했던 분들이라면 코드숨 과정이 많은 도움이 될 것이라고 생각합니다 :)
  • 만약 코드숨을 시작하셨다면 미션을 하며 코드를 많이 작성하지 못해도, 미완성일지라도 일단 그 날 저녁 9시 이전에 PR을 날리세요! 코드가 엉망이어도 주저하지 마세요..! 우리는 배워서 성장하려고 큰 금액을 투자하는 거니깐요! (전 매일 저녁 8시 52분 알람을 맞춰놓았습니다😂)
  • 개인적으로 느낀 점은 투자하는 시간만큼 얻어가는 것이 많습니다! 하지만 이건 어느 멘토링 과정이든 똑같다고 생각합니다! 웬만하면 충분한 시간적 여유를 확보하는 것을 추천드립니다 :)

8주간의 기록


OOP & Clean Code (17개)

1. final을 선언하여 변수의 재할당을 방지하자 (관련 리뷰1, 리뷰2, 리뷰3)

  • 클래스 멤버는 기본적으로 final을 선언하는 것이 좋다
  • 재할당이 불가하므로 코드를 읽을 때 불필요한 추론(변경 가능성, 초기화 등)을 안해도 된다
  • 재할당을 방지함으로써 불변성 효과를 얻을 수 있다 (참고 자료)
    • 동기화(synchronisation) 없이 안전하게 읽을 수(read) 있다 (Thread-safety)

변수 final : 재할당 방지
메서드 final : 오버라이딩 방지
클래스 final : 상속 방지 (관련 리뷰)


2. early return 방식을 사용하자 (관련 리뷰)

  • 코드의 depth가 줄어드는 장점이 있다 (가독성 상승)

  • 예외 조건이 많다면 Bouncer Pattern을 같이 사용하자

    public void doStuff(SomeObject argument1, SomeObject argument2) {
        validateArgument(argument1, argument2);
        // do more stuff;
    }
    
    private void validateArgument(SomeObject argument1, SomeObject argument2) {
        if(!argument1.isValid()) {
                throw new Exception();
        }
        if(!argument2.isValid()) {
                throw new Exception();
        }
    }

3. null일 가능성이 없는 변수에 .을 찍어 NPE를 방지하자

// 변수 path가 null일 확률도 있음
if (!path.equals("tasks"))

// null일 확률이 있는 요소를 비교 대상으로 두어 NPE를 피하자
if (!"tasks".equals(path)) {

4. If 문의 중괄호는 어느 상황이든 생략하는 것을 지양하자 (관련 리뷰)


5. 조건식이 일상어처럼 읽히도록 작성하자 (관련 리뷰)

  • 직관적으로 보이는 간단한 조건식이 아니라면 메서드로 추출하여 네이밍을 통해 일반 글 처럼 코드가 읽히도록 작성하자
// 이것 보다는
if (path.getPathVariable() == null)

// 이런식으로!
if (path.isInvalid())

// 단! 아래같이 직관적인 조건식은 굳이 메서드로 추출할 필요는 없다
if (str == null)

6. 불필요한 변수 할당을 지양하자 (관련 리뷰)

  • 변수에 담긴 표현식이 변수명과 다를바가 없다면 인라인을 하는 것이 좋다 (리팩터링 2판)

7. 의도가 명확한 코드를 작성하자 (관련 리뷰)

  • 아주 조금이라도 헷갈릴 수 있거나 다르게 해석될 여지가 있다면 사용을 지양하는 것이 좋다
  • 또한 어떻게 하는지보다 무엇을 하는지를 보이게 코드를 작성하자 (관련 리뷰)

8. 가능한 문제 해결 가능 범위 내에서 가장 좁은 범위의 타입을 사용하자 (관련 리뷰)

  • 타입이 있는 Java에서 클래스는 사용자 정의 타입이다
  • 메서드의 리턴 타입과 매개변수 타입, 변수 타입 등을 Class 사용을 통해 범위를 좁혀보자
// 반환 타입이 String일 경우 표현 범위가 너무 넓다
public String getTaskByUserId(Long userId)

// 반환 타입을 Task 객체로 사용하여 표현 범위를 최대한 좁히자
public Task getTaskByUserId(Long userId)

9. Java를 사용할 때는 타입을 적극 활용하자 (관련 리뷰)

  • Java에서는 섬세하게 나눠진 타입을 만들어 문제로 접근해갑니다.
  • 타입이 문제 해결의 핵심 도구 중 하나가 되는 거죠.
  • 더 Java 다운 코드를 작성하고 싶다면 사고 방식의 핵심에 타입을 두고 논리를 펼쳐나가거나 문제에 접근해보는 것도 훈련해 볼 필요가 있어요

10. 하나의 책임은 하나의 객체로 응집하자 (관련 리뷰1, 리뷰2)

  • 데이터만 갖고 있는 객체가 아닌, 무언가를 결정하고 스스로 생각할 수 있는 능동적인 객체를 만들도록 하자
  • 상태와 행위를 한 곳에서 관리해야 좋다 (일급 컬렉션)

11. 각 클래스는 단일 책임 원칙에 맞게 관심사를 분리하자 (관련 리뷰)

  • SRP(단일 책임 원칙)는 코드가 변경되는 이유는 오로지 1가지의 이유여야 한다
  • 클래스는 1가지의 관심사에만 집중하고, 관련 코드를 모두 해당 객체로 응집한다
  • 또한 메서드는 1가지의 행위에만 집중하고, 그 1가지를 매우 잘 수행하도록 구현해야 한다

12. setter를 지양하자

  • setter를 열어둔다면 관련 객체 멤버의 작동을 신뢰하지 못하는 순간이 올 수도 있다
  • 소프트웨어 규모가 커지면 커질 수록 불변 객체는 매우 중요해진다

13. 초기화는 한번에 수행하여 불변 객체를 지향하자 (관련 리뷰)

  • setter를 이용한 초기화(자바빈 패턴)는 언제 제대로 된 객체가 되는 지 불확실하다
  • 초기화 할 변수가 적다면 점층적 생성자 패턴, 많다면 빌더 패턴을 사용하자

14. 인터페이스와 public 메서드에는 JavaDoc 주석을 작성하자 (관련 리뷰1, 리뷰2, 리뷰3)


15. 클래스 상수(static final)일 때는 대문자 SNAKE_CASE, 인스턴스 상수(final) 및 일반 변수는 camelCase를 사용하자 (관련 리뷰)


16. 주석을 구현에 의존되지 않게 추상적으로 작성하자 (관련 리뷰)

  • 주석이 구현에 의존되게 작성한다면, 메서드의 내용이 수정되고 주석 내용의 수정을 깜빡했을 때 혼란을 유발할 수 있다

17. 객체지향에서의 의존은 곧 수정을 의미한다

  • A라는 객체가 B에 의존하고 있다면. B의 코드에 수정이 일어났을 때 A또한 수정이 일어날 수 있다
  • 이는 코드의 수정 이유가 오로지 하나여야 하는 단일 책임 원칙(SRP)을 위배한다
  • 따라서 객체 간 직접적인 의존을 피하기 위해 인터페이스를 통해 의존을 역전(DIP)시킨다

CS (1개)

1. 요청에 대한 응답 데이터가 없을 때는 204로 응답한다 (관련 리뷰)

  • 응답에 상태를 설명하는 엔티티가 포함되면 200 (OK)
    • 새롭게 업데이트한 페이지를 보여줘야 할 때 사용해야 함
  • 요청에 대한 처리가 아직 되지 않은 경우 202 (Accepted)
  • 요청에 대한 처리가 완료되었지만, 응답에 엔티티가 포함되지 않을 경우 204 (No Content)
    • 요청이 성공했으나 클라이언트가 현재 페이지에서 벗어나지 않아도 된다는 것을 나타냄

Github (2개)

1. 커밋 컨벤션과 PR 방식은 모든 회사마다 제각기 다 다르다 (관련 리뷰)

  • 어떤 회사는 커밋이 많아도 신경쓰지 않고, 어떤 회사는 머지만 사용하고
  • 어떤 회사는 기능별로 잘 분리한 커밋 목록을 선호하고, 어떤 회사는 강력하게 리베이스를 사용하도록 강제한다
  • 정말 제각각이고, 그러다보니 Best Practice 또한 애매하다
    • 종립 멘토님의 취향
      • PR 하나는 작업에 대한 보고서이자 설계이자 제안서이다
      • PR에 포함된 commit 하나하나는 해당 제안서에서 이야기하고자 하는 작업들을 이해하기 쉽게 쪼개놓은 것이다
      • 따라서 할 일 목록을 먼저 종이에 이해하기 쉽게 정리하자
        • 양이 너무 많아도 안되고 너무 적어도 안된다
      • 그 할 일 목록의 한 줄 한 줄이 커밋 메시지가 되도록 작업한다

2. PR, Commit 단위 (관련 리뷰1, 리뷰2)

  • Commit은 단순히 파일 단위로 작게 쪼개기보다는, 만들어진 기능 단위에서 작은 단위로 커밋을 생성하자 (테스트를 포함)
  • Commit 타이틀에 관련된 기능만 변경 사항에 존재해야 한다
    • Commit 타이틀에 작업한 하나의 일을 적고 그것만 들어있도록 하는 것이 시작이다
  • 리뷰가 쉽지 않은 이유는 대부분 커밋의 개수보다는 변경된 파일의 개수 때문이다
    • 실무에서는 PR의 크기 자체가 작기 때문에 일반적으로 하나의 PR에 많은 File Change가 있지 않을 가능성이 높다
  • PR에도 SRP를 적용하는 것이 좋다 (참고 자료)

Java (2개)

1. 동시성을 고려해야 할 때는synchronizedAtomic클래스를 사용하자 (관련 리뷰)

  • synchronized : lock을 통한 쓰레드 동기화
    • synchronized 메서드는 클래스의 인스턴스 단위로 동기화가 일어난다
      • static synchronized 메서드는 해당 클래스 단위로 동기화가 일어난다
        • id도 같이 static으로 설정함으로써 static 메서드와 동기화가 이루어진다
    • Atomic : 내부의 volatile로 선언되어 있으며, 이를 통해 코어가 변수의 값을 읽어 올 때 캐시가 아닌 메인 메모리에서 읽어옴으로써 멀티 쓰레드 환경에서 값의 불일치가 해결됨

2. 정규식을 사용할 때는 기차 레일 다이어그램을 그리거나, 정규식을 검증하는 테스트 코드를 PR에 포함시키자 (관련 리뷰)

  • 그렇지 않으면 리뷰어(동료)가 힘들어하거나 대충 보게 된다
  • 기차 레일 다이어그램 추천 사이트 - https://regexper.com

Spring (4개)

1. 생성자가 1개라면 @Autowired를 생략해도 암묵적으로 DI가 진행된다 (관련 리뷰)

  • Spring 4.3 부터 단일 생성자를 가진 클래스는 @Autowired 생략 가능 (Baeldung)

2. 응답 반환 시 ResponseEntity를 따로 사용할 필요가 없다 (관련 리뷰)

  • Spring Web MVC에서는 객체를 반환하면 응답 바디로 세팅된다
  • HTTP 응답 코드를 설정하려면 @ResponseStatus를 사용하면 된다

3. 예외를 어드바이스로 핸들링을 할 때는 도메인 별로 분리해주는 것이 좋을 수 있다 (관련 리뷰)

  • 보통 도메인 별로 팀이 나눠지는 MSA로 이루어진 회사들은 각 팀마다 예외 핸들링을 따로 할 것이다
  • 이러한 관점에서 도메인 별로 예외 어드바이스(@ControllerAdvice)를 분리하는 것이 좋을 것 같다 (굳이 혼자 하는 토이프로젝트에선 안해도 되겠지만…)

4. Spring Aop (관련 리뷰)

  • 핵심 관심사가 아닌 부가적인 공통 관심사를 따로 분리하여 핵심 비즈니스 로직이 오염되지 않도록 도와주는 기능이다 (객체 지향 프로그래밍(OOP)을 더 잘할 수 있도록 도와줌)
  • 공통 관심사 로직이 있는 메서드에 Advice와 Pointcut을 설정해준다 (참고 자료)
    • Advice : 어드바이스 메서드가 동작할 시점
      • ex) @Before(메서드 실행 전), @After(메서드 실행 후), @Around (메서드 실행 전 후)
    • Pointcut : 어드바이스 메서드가 동작할 기준
      • ex) @LogExecutionTime이 주석으로 달린 모든 메서드에 이 Advice를 적용하라

TEST (11개)

1. @DisplayName을 통해 테스트의 목적과 기대하는 결과에 대한 설명을 작성하자 (관련 리뷰)

  • @DisplayName이나 @DisplayNameGeneration을 통해 무슨 테스트인지 직관적으로 볼 수 있도록 해야 한다
  • 계층 구조로 테스트를 작성할 때 상단 테스트 설명과 자연스럽게 이어지는 문장이 되도록 작성해야 한다

2. @BeforeEach를 통해 테스트를 준비하자 (관련 리뷰)

  • 믿고 사용할 수 있는 Fixture 데이터를 준비하자
  • 테스트 준비와 검증을 따로 분리하여 각 메서드의 역할을 분리해주자

3. 테스트를 구조화하여 작성하자 (관련 리뷰)

  • @Nested를 통해 계층 구조를 만들 수 있다 (참고 자료)
  • Describe-Context-It 또는 Given-When-Then 패턴을 통해 BDD(행동 주도 개발)를 수행하자
    • 사용자의 행위를 작성하고, 이를 검증하는 방식으로 테스트 코드를 작성
      • 기획서와 코드 설계가 동기화 되는 효과
      • 서비스의 이해도가 증가 되는 효과
    • BDD를 통해 시나리오 테스트를 진행하고, TDD를 통해 각 기능의 동작 테스트를 진행하자 (BDD는 TDD에서 파생된 테스트 : 상호 보완적 관계)


4. BDD를 진행할 때 테스트 설명은 각 역할에 맞게 명시하자

  • Describe절 : 테스트 대상이 무엇인지 명시
    • ex) create 메서드는
  • Context절 : 테스트 대상에게 무엇이 주어지는지, 어떤 환경이 제공되는지 명시
    • ex) ~ 경우, 파라미터가 없다면 ~ 때, 파라미터가 있다면 ~ 주어지면 등등
  • It절 : 테스트에 기대하는 결과 값을 명시

5. 테스트를 작성할 땐 검증해야 할 요소를 정확히 먼저 파악하자 (관련 리뷰1, 리뷰2, 리뷰3)

  • 중요하지 않거나, 불필요한 테스트 작성을 지양하자
  • 제품 가치에 대한 테스트는 통합 테스트로, 기능의 동작 테스트는 단위 테스트로 검증한다
  • 데이터 검증을 너무 복잡하게 분리하지 말자
    • Enum Fixture로 검증할거면 일일히 필드 하나하나 비교 하지말고 객체 비교를 하자

6. JPA 기능, Builder, 간단한 getter, setter 등은 굳이 테스트 코드를 작성하지 않아도 된다

  • 널리 사용되고 검증된 라이브러리는 보통 굳이 테스트하지 않는다
  • ps. JPA의 핵심 코어 로직 관련 테스트만 4000개 이상이라고 한다…

7. 테스트 실패 시 에러 메시지를 표출해야 할 때, 메시지 작성 비용이 많이 든다면 Supplier<String>을 사용하자 (관련 리뷰)


8. 기계적인 테스트 커버리지는 중요하지 않다 (관련 리뷰)

  • 커버리지 수치에만 매달릴 경우 오히려 코드 품질이 저하될 수 있다 (참고 자료)
  • 핵심 기능들을 모두 테스트 + 운영 중에 발견된 버그에 대해 테스트 코드를 추가하여 버그 재발을 방지
    (버그에 대한 테스트 코드를 쌓아가자)
  • 위 두가지를 신경쓰는 것이 기계적으로 커버리지를 챙기는 것보다 합리적으로 운영할 수 있을 것 같다
  • 테스트 커버리지는 테스트 품질이 얼마나 좋은지에 대한 수치가 아닌, 테스트되지 않은 부분을 찾기 위한 도구이다 (참고 자료)

9. 테스트 코드에서는 가독성을 위해 한글을 사용해도 좋다 (관련 리뷰)

  • 메서드 이름 등에 한글을 사용하면 코드를 한결 읽기 좋아진다

10. Mock은 꼭 필요한 상황에서만 의도적으로 사용하고, 웬만하면 지양하자 (관련 리뷰)

  • Mock의 장점
    • 느린 DB연결 및 API 호출을 대체하므로 테스트가 빨라진다 (DB는 Fake로 커버 가능)
    • 외부 모듈로 인해 테스트가 실패하는 경우가 없으므로 안정적이다
    • 따라서 Mock 테스트의 given은 팀이 직접 제어할 수 없거나 접근하기 매우 어려운 외부 모듈 같은 곳에 사용하는 정도가 적절하다
      • 외부 기상청 API 호출하는 비즈니스 로직
      • 일 호출 횟수 제한이 있는 외부 API 등
  • Mock의 단점
    • Mock을 사용하면 given을 통해 원하는 행위를 재정의하므로, 테스트 코드를 완벽하게 신뢰할 수 없다
    • 행위 검증 방식이기 때문에, 메서드 구현 내용(협력 객체)이 수정된다면 관련 테스트 코드도 다시 수정해주어야 하는 번거로움이 발생한다 (테스트 코드 관리 비용 발생)
  • 따라서! 대안책으로는 실객체 or FakeObject를 사용하는 것이고, 실제 구현체 및 Fake를 만들 수 없는 시나리오(제어할 수 없는 영역)일 때만 Mock을 권장한다 (참고 자료)
    • Mock : 실제 객체의 행위를 재정의
    • FakeObject : 실제 객체가 동작한 것처럼 출력값이 전달되도록 가짜 객체를 만드는 방식

11. TDD는 단지 방법론일 뿐이다 (관련 자료)

  • TDD는 테스트 코드를 먼저 작성해서 테스트가 실패할 수 밖에 없는 상황을 만들고, 테스트 코드의 피드백을 받으면서 코딩하며 테스트가 성공하면 또 다른 테스트 코드를 추가하는걸 반복하는 방법론일 뿐이다
  • 따라서 TDD는 MVC 패턴의 R/S/C와 무관하므로 개념을 분리해서 생각해야 한다
    • MVC 구조에서 Controller 테스트는 보통 Service를 통하여 이루어지므로 개발 순서와 관련이 있다고 혼동할 수 있지만 이 때는 Test Double(Mock, Fake 등)을 사용하면 되는 부분이므로 근본적으로 TDD가 개발 순서에 영향을 주는 것이 아니다
    • 따라서 Controller → Service → Repository 순으로 개발하냐, 반대로 개발하냐의 개발 순서는 TDD 방법론과는 별개의 이야기이므로 혼동하지 말자 (정리 내용)
  • 또한 테스트 코드를 성공 케이스를 먼저 작성하든 실패 케이스를 먼저 작성하든 상관 없으니 제일 먼저 생각나는 테스트 케이스를 작성하자

Architecture (4개)

1. Domain vs Service (관련 리뷰)

  • 본래 알고 있었던 부분 : service는 비즈니스 로직, domain은 데이터를 담당하는 객체(엔티티)

  • 인터페이스를 통해 각 계층 간 입/출력 약속을 정의하고 의존성을 역전시킨다

패키지 구조로 예를 들면
- application 패키지 : service
- domain 패키지 : entity, repository(영속성 repository의 인터페이스)
- infrastructure 패키지 : repository를 확장하는 영속성 레파지토리

  • Application (Application Business Rules)
    • 데이터의 흐름을 조정하고 해당 엔티티(domain)가 목표를 달성하기 위해 비즈니스 규칙(도메인)을 사용하도록 돕는다 (도메인 객체가 할 일을 하도록 연결만 해주는 역할)
    • 도메인 로직이 의사결정을 할 수 있도록 입력을 제공하고, 결과를 외부(Service/DB/UI)에 업데이트 하는 역할을 맡는다 (관련 자료)
      • ex) update할 원본 상품을 id로 DB에서 가져오는 로직
      • ex) Product를 update하기 전 해당 상품이 존재하지 않을 때 null을 던지는 로직
  • Domain (Entity)
    • 도메인은 개념적인 용어(명사)이고, 도메인을 구현하는 방식 중 하나가 엔티티이다
      • 모든 엔티티에 대한 변경(비즈니스 로직)은 도메인 모델에서만 이루어져야 한다
    • 비즈니스에 대한 의사결정을 하는 로직들을 도메인 로직이라고 말한다 (비즈니스 로직)
      • ex) Product 엔티티 내부에 있는 update 메서드 로직 (의사 결정: 상품 수정)
    • 어떠한 의존성도 없어야 하는 것이 원칙이지만 repository의 경우에는 포트를 이용해 어댑터(영속성)를 주입받아서 사용

2. Hexagonal-Architecture (관련 리뷰1, 리뷰2)

  • 헥사고날은 도메인 엔티티와 유스케이스를 중심으로 각 외부(컨트롤러, DB) 어댑터를 통해 요청을 받는다

  • 헥사고날의 특징은 모든 의존 방향이 도메인 엔티티로 향하기 때문에, 어떤 외부 요소의 변경 사항에도 도메인은 영향을 받지 않는다 (수정이 일어나지 않음)
  • 또한 port(인터페이스)를 통해 직접적인 의존을 피하므로, 어댑터를 갈아끼우기 쉬운 아키텍처이다
    • 어댑터를 갈아끼우기 쉬워야 하므로 비즈니스 로직과 어댑터를 물리적으로 독립시켜야 한다 (관련 리뷰)

3. 각 계층 별 역할 정리 (일반적인 MVC 기준) (관련 리뷰)

  • Presentation Layer
    • 외부 환경이 유스케이스를 사용하기 위해 데이터 형태를 알맞게 변경하여 넘겨(호출)주는 역할을 한다 (외부 환경과 유스케이스를 연결해주는 어댑터 역할)
    • 또한 유스케이스가 목표를 달성하고 반환한 값을 외부 환경에서 필요한 데이터 형태로 변경해주는 역할을 맡는다 (엔티티가 그대로 반환되지 않도록 주의!)
    • 외부 환경에서 넘겨 받은 데이터를 검증한다 (Validation)
    • 외부 환경과 유스케이스의 연결 다리 느낌이므로 비즈니스 로직이 존재하는 것은 어색해보인다
    • ex) Controller
  • Business Logic Layer (Application, Domain)
    • 비즈니스 로직과, 유스 케이스의 목적을 달성하기 위한 부가 로직들을 실행하는 역할을 한다
    • 보통 Service 상단에서 넘겨받은 Dto를 엔티티로 변환한다
    • 비즈니스 로직 도중 발생하는 예외를 던지는 역할을 한다
      • 비즈니스 로직에 필요한 데이터 값들이 유효한지 검증 (논리적 오류)
    • ex) Domain Entity, UseCase, Service, Repository(영속성 계층이 구현해야할 인터페이스)
  • Persistance Layer
    • 영속성, 즉 데이터 처리를 담당하는 역할을 한다
    • ex) JPA Repository, MyBatis Repository

4. DTO ↔ Entity 사용 흐름 예시 - 종립 멘토님 방식 (관련 리뷰1, 리뷰2, 리뷰3)

  • Controller → Service
    • Controller에서 Service로 넘길때의 Command를 인터페이스로 생성 (ProductCreateRequest)
      • 해당 인터페이스에는 getter만 존재
    • Controller에서 사용하는 DTO는 Command 인터페이스를 구현 (ProductCreateRequestDTO)
    • Service는 요청 데이터를 전달받을 때 Command Interface를 입력받음
  • Service → Controller
    • Service에서 Controller로 넘길 때는 Entity를 그대로 반환
      • 컨트롤러 안쪽까지는 Entity의 통제 범위라고 생각
    • Controller 바깥으로 반환(응답)할 때 Entity의 wrapper가 되도록 DTO를 설계
      • DTO has a Entity
      • DTO는 Entity 멤버 하나만을 받는 생성자를 생성 (Entity 멤버는 @JsonIgnore 처리)
      • 해당 Entity에서 반환에 필요한 요소들만 getter로 추출 (관련 내용 정리)
        • Response DTO를 일종의 응답 데이터 반환용 소규모 repository로 사용
        • DTO는 원본 데이터를 특수하게 비틀어 보여줄 뿐인 view가 되는 느낌

교양 (4개)

1. 공식 문서를 통한 학습 방법

=> 공식문서를 통해 배운 것을 써보기
=> 써본 것을 공식문서를 통해 배우기

  • ~~ 문제가 있어서 ~~를 해결하기 위한 방법이라고 가정을 세운다
  • 문제를 재연한 후 공부한 방법을 적용한다
  • 문제가 해결되는 것은 증명한다
    • 문제 해결이 실패하면 왜 잘못 사용한건지 파악한 후, 다시 새로운 해결 방법 가정을 세운다

2. 내용 분석 및 문제 해결 과정 방식 (관련 리뷰)

  • 문제 상황 - 원인 분석 - 해결 과정 - 참고 자료
    • 문제 상황을 명시 - 해결해가는 과정 - 출처 순으로 기록을 남기면서 다양한 문제를 해결해나가자
  • 혼자 생각하고, 실험하며 완성된 글로 남기는 경험은 매우 중요하다
  • 자기 자신을 과학자라고 생각하고 문제에 접근하자
    • 대상을 정의하고, 실험을 설계하고, 실험을 실행해 본 다음 결과까지 기록하고, 그 결과를 누구나 볼 수 있는 곳에 남겨놓자
    • 자신의 사고 방식과 일하는 방식을 더 정교하고 깊이있게 만들어 줄 거에요.

3. 라이브러리를 도입, 교체할 때는 침투율과 제거 시나리오 등을 생각하자 (관련 리뷰)

  • 회사에서 어떤 기술을 도입하는 것을 주도할 때는 동료들을 설득해야 한다
  • 따라서 관련 라이브러리를 어디 범위까지 사용할건지, 해당 라이브러리를 들어내야 할 때의 제거 시나리오 등을 정리(보고서 작성)하는 연습을 해보면 좋을 듯 하다

4. 기술에 정답은 없다

  • 어제의 정답이 오늘의 오답이 되는 것이 기술이다
  • 정답을 찾는 것이 아닌, 상황에 맞게 트레이드 오프를 고려해 유연하게 기술을 적용하자

Point Pick (7개)

열심히 성장하는 추억(?)을 모아보자!

1. [1주차] Spring 없이 순수 Java로 API 개발 설계도


2. [2주차] 계층 별 역할 및 책임 정리


3. [2주차] 예외 핸들링 설계


4. [3주차] 첫 기술 관련 의사결정 과정

https://github.com/CodeSoom/spring-week3-assignment-1/pull/89#discussion_r1006430973


5. [6주차] 라이브러리 교체 의견 : Dozer → Mapstruct (관련 리뷰)


6. [6주차] @LoginRequired (관련 리뷰)


7. [8주차] Hexagonal Architecture 의견 (관련 리뷰)

profile
꾸준함의 가치를 향해 📈

0개의 댓글