1. 나는 왜 이 과정을 듣게 되었나?
- 나의 환경
필자에 대해 간략하게 소개하자면 2년차 개발자로 작년 8월부터 프로젝트 하나를 팀원들과 함께 맡게 되었다. 필자가 맡은 프로젝트는 BtoC 프로젝트로, 런칭 전 개발을 진행하였고 런칭 후 현재 유지보수 및 운영업무를 진행하고 있다. 런칭 후 대고객 서비스가 그러하듯 예상치 못한 부분에서 에러가 발생하고 수정하는 경우가 다반사였다. (런칭 초기에는 하루에 한번씩 배포를 진행했다) 잦은 수정이 이뤄지다보니 항상 부수효과를 마주하는 나 자신을 보게 되었다.
- TDD?
실무 생긴 이러한 문제를 어떻게 해결할 수 있을까 고민하다 보니 어렵지 않게 TDD라는 개발방식에 관심을 갖게 되었고, 신규 기능 개발 요건이 있을 경우 나의 태스크만이라도 TDD 개발 방식으로 개발하게 되었다. 하지만 TDD는 혼자 해서는 아무런 의미가 없다. 팀원이 프로젝트에 이슈가 생겨 코드를 수정할 경우 어느 순간 내가 작성했던 테스트는 작동하지 않는다. 또한 나는 주로 Spring Slice Test 라는 기술을 주로 사용했는데 이러한 테스트는 작성할 수록 테스트 수행과 빌드가 오래 걸렸고 결국 조금씩 TDD에 대한 열정은 사그라들었다.
- Clean Code?
운영 및 유지보수 업무를 진행하다보면 크리티컬한 장애가 발생할 경우 신속하게 이슈를 핸들링해야 하는 경우가 자주 생기게 된다. 지금은 리팩토링을 해서 많이 없었졌지만 런칭 초기에는 이런 코드도 많았다.
...
if(ServiceCategory.VSWASH == serviceCategory) {
...에러 방지 로직...
}
...
필자는 이러한 코드는 정말 삼가해야 할 절차지향적인 코드라고 생각한다. 이러한 코드를 작성하지 않기 위해 Effective Java, Clean Code 등의 책을 읽으며 수정했지만 회사 특성상 코드에 대한 피드백은 얻을 수 없기에 이러한 코드가 효율적이고 좋은 설계인지에 대한 확신은 얻을 수 없었다.
- NEXTSTEP?
이렇게 고민하던 찰나에 개발관련 자료를 찾아보던 중 NEXTSTEP을 알게되었다. 커리큘럼과 교육방식을 보았을 때 나의 이러한 궁금증을 코드리뷰를 통해 해소할 수 있을 것 같아 (나에게는)거금 80만원을 쾌척하여 시작하게 되었다. 수강신청이 어려울 것이라고 생각했지만 그렇게 어렵진 않았고 수강신청 당일에만 신청하면 수강대기를 할 필요는 없다.
2. 과정 설명
과정을 간단하게 설명하자면 4개에 미션당 4~5 Step이 주어지며 수강생은 하나하나씩 풀어가며 PR을 날린다. PR을 올릴 경우 개인에게 할당된 담당 리뷰어가 코드리뷰를 남긴다. 상세한 정보는 여기에서. 한가지 TMI를 주자면 리뷰어는 대부분 현직 네카라쿠배 개발자 혹은 우테코 수강생 분들로 구성되어있으며 해당 과정을 수료한 사람 중 가장 성적이 우수한 사람에게 리뷰어 기회가 주어진다.
3. 나는 무엇을 배웠는가?
- OOP
객체지향이란 Java 개발자에게 있어 평생의 숙제라고 생각한다. 그렇게 객체지향에 대해 고민하며 공부를 하다보면 첫번째로 마주하게 되는 책이 있다. 바로 ‘객체지향의 사실과 오해’라는 책이다.
이 책에서 강조하는 개념들을 나열해보자면
1. 역할, 책임
2. 메세지
3. 결합도와 응집도
객체간의 책임을 최대한 작게 유지하고 메세지를 통해 객체의 능동적인 역할을 고려하다보면 자연스럽게 결합도는 낮아지고 응집도는 높아지는 설계를 할 수 있게 된다. 이러한 개념들을 고려하여 클래스 설계를 해보면 자연스럽게 서비스에 모든 로직이 모여있는 설계가 객체지향보다는 절차지향에 가깝다는 느낌을 받게 되고 자연스럽게 로직은 도메인 클래스에 모이게 된다. 이렇게 도메인 클래스에 로직이 응집되어 있을 때 내가 생각하는 장점은 아래와 같다
🥕 Test Code 작성에 용이하다
기존 레이어 아키텍쳐로 클래스를 설계할 경우 서비스단에 비지니스 로직이 모여있기 때문에 테스트 코드 작성이 어려운 코드와 쉬운 코드가 섞여있어 테스트 코드 작성에 어려움이 생긴다. 테스트 코드 작성이 어려운 코드를 예제를 통해 확인해보자. 예제는 실제로 교육기간에 과제로 주어진 코드이다.
@Transactional
public void deleteQuestion(User loginUser, long questionId) throws CannotDeleteException {
Question question = findQuestionById(questionId);
if (!question.isOwner(loginUser)) {
throw new CannotDeleteException("질문을 삭제할 권한이 없습니다.");
}
List<Answer> answers = question.getAnswers();
for (Answer answer : answers) {
if (!answer.isOwner(loginUser)) {
throw new CannotDeleteException("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다.");
}
}
List<DeleteHistory> deleteHistories = new ArrayList<>();
question.setDeleted(true);
deleteHistories.add(new DeleteHistory(ContentType.QUESTION, questionId, question.getWriter(), LocalDateTime.now()));
for (Answer answer : answers) {
answer.setDeleted(true);
deleteHistories.add(new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now()));
}
deleteHistoryService.saveAll(deleteHistories);
}
위 코드의 문제점은 DB에 의존하는 로직들로 인하여 테스트코드를 작성할 경우 Mocking을 해야만 하는 어려움이 있다. 테스트 코드에 Mocking하는 부분이 많아질수록 Test Fixture가 많아지게 되므로 테스트 코드 작성에 피로도가 높아지게 된다. 이러한 점을 고려하여 도메인 중심으로 비지니스 로직을 옮기게 되면 아래와 같이 코드를 리팩토링 할 수 있다.
@Transactional
public void deleteQuestion(User loginUser, long questionId) throws CannotDeleteException {
Question question = findQuestionById(questionId);
DeleteHistories deleteHistories = question.delete(loginUser);
deleteHistoryService.saveAll(deleteHistories.getDeleteHistories());
}
테스트 코드 작성의 난이도가 현저히 낮아졌다. Question 도메인과 하위 도메인을 위주로 테스트하면 되기 때문에 기존 deleteQuestion
메소드를 테스트해야 하는 것 보다는 분할 로직에 대한 테스트 코드 작성이 가능해진다. 로직의 재활용도 또한 올라갈 것이다.
🥕 진정한 CI/CD의 구현
테스트 코드의 작성이 용이해질 경우 자연스럽게 테스트 커버리지는 올라간다고 생각한다. CI/CD 이름에는 나와있지 않지만 Continuous Testing 또한 CI/CD의 굉장히 중요한 부분을 차지한다. 도메인 위주 클래스 설계를 통해 테스트 코드 커버리지가 올라갈 경우 CI/CD 툴을 이용한 빌드를 할 때 미연에 에러를 잡아주어 미래의 우리의 고생을 줄여줄 수 있다고 생각한다.
🥕 MSA 전환 용이
비지니스 로직을 도메인 중심으로 설계할 경우 도메인간의 결합도를 낮출 수 있기 때문에 추후 Monolithic Architecture에서 MSA로 설계 이전이 필요할 경우 기존 레이어 아키텍쳐보다 손쉽게 도메인을 분리하여 모듈로 설계가 가능하다.
- TDD
필자가 혼자 집에서 연습 혹은 실무에서 적용해본 TDD는 대부분 Spring Slice Test였다 그렇기 때문에 Mocking을 해야하는 경우가 굉장히 많았으며 Test Fixture 또한 굉장히 많았다. 그러나 넥스트스텝 과정에서 필자는 테스트가 어려운 로직을 분리하는 방법, 도메인으로 로직을 분리하는 방법에 대해 알게 되었다. 이를 통해 Mocking이 많이 필요 없고 Framework(Spring)에 대한 의존도 또한 현저히 줄어들어 유연한 테스트 코드 작성이 가능하게 되었다.
- Soft Skills
수강생 대부분이 그러하듯 넥스트 스텝에서 가장 기대했던 것은 코드리뷰였다. 하지만 처음으로 받아본 코드리뷰는 마냥 좋지는 않았다. 처음 PR에 대한 코멘트가 날아왔을 때 기대 반 걱정 반으로 확인했던 것 같다. 물론 나의 코드가 완벽하지 않고 피드백 또한 너무 감사하지만 피드백이 왔을 때 작성한 코드에 대한 아쉬움은 어쩔 수 없었다.
