3주 회고록을 작성하지 못했던 것에 대한 회개
제가 백엔드 프로젝트에 몰두한다는 핑계로 3주차 회고록을 작성하지 못했는데요.... 저는 회고를 작성하는 시간이 제가 공부한 것들과 생각한것들을 정리하는 시간인데, 회고를 작성하지 못했다는 것은 대략 4주정도 쌓기만 하고 정리는 못했다는 것을 말합니다.
쌓는 것보다 정리하는 것이 더 중요하다는 것을 알고 있는데도 시간이 없다는 핑계로 이전의 악습관을 반복하는 저의 모습을 보며 아쉬움을 크게 느꼈고 이것을 어떻게 하면 고칠 수 있을까 생각하다 역시 어느정도의 제한을 걸어야 악습관을 끊을 수 있을 것 같아 14주차부터 부트캠프 끝날때까지 한주라도 회고록을 밀리면 한 주 밀릴때마다 모든 동기분들께 커피를 돌리면 좋지 않을까 싶습니다.
제한을 강하게 건 만큼 다음주부터 부트캠프 끝날때까지 절대 회고록이 밀리지 않는 저의 모습을 많은 관심과 사랑으로 기대해주시길 바라겠습니다.
백엔드 프로젝트에 대하여
이번 백엔드 프로젝트에 대한 자세한 내용은 사실 백엔드 프로젝트 회고에서 이미 기술한 바 있는데요.
DB 프로젝트 회고와 동일하게 이번 프로젝트에서 무엇을 했는지 뿐만 아니라 어떤 생각을 했고 어떤 것에 어려움을 느꼈고 뭐가 아쉬웠고 어떻게 해결했는지를 최대한 자세하게 작성해 놓았으니 한번 들어가서 봐주시면 정말 감사할 것 같습니다.
여기서는 짧게 얘기해보자면 이번 프로젝트에서 가장 크게 도움이 되었던 것은 DDD, 테이블 정의서, 테스트 케이스 정의서였는데요. 해당 산출물들은 설계와 기능 구현의 방향성을 잡아주는데 강력한 무기였습니다.
처음에 DDD 설계를 "Why" 해야하는지 감조차 잡을 수 없었는데요. 37개의 테이블을 설계할때까지만 해도 몰랐는데 CQRS 패턴 구조로 프로젝트 구조를 잡을 때 이걸 어떻게 설계해야할지 캄캄해서 10기 선배님들의 프로젝트 구조를 많이 확인했고 그 비로소 "도메인"이라는 것이 무엇인지 이해하게 되었고 DDD 설계를 통해서 자연스럽게 맥락을 나눴던 것이 왜 필요했던 작업인지 깨닫고 만약에 그 작업을 하지 않았더라면 테이블별로 디렉토리를 나눠서 37개의 도메인과 37개의 마이크로서비스가 나올 수도 있었다는 것을 알게되며 이마를 탁치게 되었습니다.
클린코드 아키텍처 책을 읽어보면 뭐만하면 응집도를 높이고 결합도를 해소하는 내용에 대해 나오는데 도메인을 중심으로 모듈을 나누면 의존도를 조절할 수 있구나라는 감각도 생기게 되고 역시 직접 겪어봐야 체득이 되는 것 같습니다.
테이블 정의서도 처음 작성하는게 매우 귀찮았는데 한번 작성하니 모두가 믿을 수 있는 단일 소스가 되었고 작성하는 과정에서 테이블에 대한 이해도도 높아지고 DDL 수정이 있을 경우엔 해당 테이블에 대한 정의서만 변경하면 되니 유지보수가 매우 쉬웠지는 것을 확인할 수 있었습니다.
테스트 케이스 정의서도 처음엔 매우 작성하는게 귀찮았는데 한번 작성하고 보니 누가 이 API를 구현 중이고 구현한 api는 무엇인지에 대해 한눈에 볼 수 있으니 테스트 케이스 정의서는 일종의 API 개발 관리표처럼 작동하여 개인적인 만족도가 매우 높았고, 다음 프로젝트에서도 무조건 도입하면 좋지 않을까 싶습니다.
아쉬운점
저는 이번 프로젝트에서 단순히 기능을 구현하는 것을 넘어 "왜 이 구조여야 할까?", "유지보수성이 좋은 코드인가"에 초점을 맞춰 최대한 안정적이고 확장성높고 유지보수성 높은 백엔드 프로젝트를 구현하고 싶었습니다.
하지만 시간이라는 현실적인 제약 앞에서 제가 추구한 구조화의 한계가 명확하게 드러났습니다.
Redis 기반 캐시 처리, RabbitMQ 기반의 비동기 메시징 구조, Prometheus + Grafana 모니터링까지, 저만의 인프라 로드맵이 머릿속에 있었습니다. 그런데 실제 구현 과정에서 우선순위가 밀리는 상황이 생겼고, 한정된 시간 내에 ‘완성도 높은 하나’를 만드는 것이 더 중요하다고 판단하여 모놀로식으로 필수적인 기능을 구현한 다음엔 MSA를 구현하다보니 3주란 시간이 송두리채 사라져있었습니다...
물론 프론트엔드 프로젝트까지 아직 시간이 남아있기 때문에 원래 계획했던 기술들을 적용해볼테지만 Redis나 RabbitMQ와 같은 Docker를 필수적으로 설치해야 하는 기술의 경우엔 다음 데브옵스때 구현해야 하지 않을까 싶기도 합니다만 파이널 프로젝트에선 모두 구현해볼 생각입니다.
그리고 저는 어쩌다보니 팀을 리드하는 역할을 맡게 되었는데 다른 팀원의 트러블슈팅을 돕는데 큰 어려움을 느꼈고 해당 어려움을 해결하기 위해 트러블 슈팅과 관련한 문서화를 제공했지만 다른 분들이 이해하기엔 용이하지 못했던것같아 앞으론 예시와 시각적인 자료를 기반으로한 이해를 중심으로한 문서화를 하는 능력을 길러야 할 것 같습니다.
프로젝트 구조 설계
저는 과거 프론트엔드 프로젝트를 진행하면서 컴포넌트 파일이 여기저기 순서와 규칙없이 나돌아 댕기고 중복되는 코드가 계속해서 쌓이고 나중엔 코드를 수정하는 것보단 새로 프로젝트를 시작하는게 나을 수준이 되는 것을 보면서 프로젝트 구조 설계에 집착하게 되었고 책임을 분리하는 것에 대한 필요성을 느끼게 되었습니다.
그래서 이번 백엔드 프로젝트 시작하기 전부터 어떻게 프로젝트 구조를 설계할까 많은 고민을 하는 와중에 클린코드 아키텍처 책에서 어떠한 도움을 주지 않을까 열심이 읽었는데 클린코드 아키텍처는 "클린코드"를 위한 아키텍처이지 "구조"를 위한 아키텍처가 아니라는 것을 깨달으며 별 정보를 얻을 수 없었는데 마침 강사님께서 CQRS 패턴 구조를 알려주셨습니다. 저는 그 구조를 보면서 이마를 또 탁 치게되었는데요. 생각해보니 코드를 크게 나누어보면 읽거나 쓰는 것이고 읽거나 쓰는 것에 대한 책임이 다르므로 구조도 다를 수밖에 없고 실제로 나눠보니 파일이 명확하게 정리되고 도메인별로 업무분담 했을 때 서로 충돌하는 파일이 적어지다보니 병합 과정에서 문제가 많지 않다는 것을 확인할 수 있었습니다.
Command측 구조에서 쉽게쉽게 하면 Application layer/Domain layer/infrastucture로 나누지 않아도 되긴 하는데 저는 이후 확장성과 유지보수성을 생각해보면 나눠놓는 것이 좋다고 생각하였고 해당 예상은 적중했습니다.
왜냐하면 제가 Application layer의 Service에서 어쩌다보니 중복되는 코드를 계속해서 작성하게 되었는데 중복되는 코드를 한곳에서 관리하기 위해 util 메서드를 만들어야 하나 고민하던 찰나 Domain 내의 Service가 생각나게 되었고 중복되는 코드들을 Domain Service에서 관리하다보니 자연스럽게 Application Service에선 Repository에 접근하지 않게 되고 메서드별로 필요한 Repository가 다르면 다를수록 응집도가 낮아진다고 클린코드 아키텍처에서 배운바 있는데 해당 문제도 자연스럽게 해결되는 것을 확인할 수 있었습니다.
공통 응답 및 예외처리 구조
저는 공통 응답(ApiResponse, ErrorResponse)과 예외 처리(GlobalExceptionHandler, BusinessExcpetion, ErrorCode)로직을 모두 시스템화 했는데요. 제가 프론트엔드를 하면서 가장 화가났던 부분이 백엔드 응답의 일관성 부족의 문제였습니다. api별로 파싱해야 하는 방법이 달라지면서 파싱 코드를 모듈화하기도 어렵고 일관성이 없다보니 매번 Swagger를 정독해서 데이터를 추출해야 했습니다.
그래서 저는 이제 데이터를 전달해주는 입장이 되었으므로 예측 가능한 응답 구조를 제공하는 것을 중요하게 생각했고 모든 응답은 ApiResponse로 감쌌고, 모든 예외는 ErrorResponse를 반환하도록 GlobalExceptionHandler를 구성했습니다.
이건 단순히 깔끔한 구조가 아니라, 프론트와의 협업을 위한 배려이자 시스템 설계의 일환이라고 생각합니다.
그리고 여기서 중요한 걸 또 하나 배웠는데 공통 코드에 너무 많은 걸 넣으면 병합이 꼬인다는 것입니다. 저는 ErrorCode Enum 하나에 모든 에러에 대한 Error Enum 타입을 관리하도록 구성했는데 그렇다보니 팀원별로 필요한 ErrorCode를 하나씩 추가하고 수정하다보니 병합과정에서 계속해서 소리소문없이 사라지는 문제가 발생하는 것을 보며 아 팀원끼리 사용하는 공통된 파일은 최대한 변경되지 않도록 해야 하고 확장가능한 방법으로 설계를 해야 한다는 것을 다시한번 깨닫게 되었습니다.
보안과 Spring Security
처음에는 Spring Security 개념자체를 이해하는것도 너무 어려워서 Spring Security만 구현하면 보안이 완성이 되는 줄 착각했는데 구현하고 보니 Spring Security는 그저 인증/인가를 컨트롤 하는데 조금 더 쉽게 컨트롤할 수 있게 해주는 도구라는 것을 알게되었습니다.
그래서 어떻게하면 보안을 올릴 수 있을까 생각해봤는데 우선 수업에서 배운 가장 basic한 방법인 jwtToken을 accessToken과 refreshToken을 두개를 만들고 accessToken의 유효시간을 짧게 해서 인증 토큰을 관리하는 것을 구현하긴 했는데 생각해보니 accessToken의 유효시간을 10분이라고 설정했을 때 로그아웃 이후에 accessToken으로 언제든지 웹사이트에 들어올 수 있는 것을 확인하게 되었고 그래서 로그아웃 이후에 기존에 존재하던 블랙리스트 테이블에 로그아웃한 회원을 집어넣고 로그인을 하게 되면 블랙리스트 테이블에서 제거하는 것으로 api에 접근하지 못하게 하였고 매번 api 호출시마다 DB에서 검사를 하는 것은 성능 오버헤드가 발생할 여지가 많기 때문에 추후에 Redis를 사용하여 가볍고 빠르게 세션으로 관리하는 것으로 변경할 생각입니다.
MSA 구현과정
저는 제가 맡은 User 도메인을 별도의 마이크로서비스 서버로 분리하고 Spring Cloud Gateway를 단일 진입점으로 두고, 인증 책임은 Gateway가, 인가는 각 서비스에서 담당하게 구성하였습니다. 이는 책임의 명확한 분리를 실현하 것이라고 할 수 있습니다. 이 과정에서 Eureka를 통한 서비스 레지스트리와 FeignClient를 도입하여 서비스 간 통신을 유연하게 만들었고, 추후 수평 확장을 고려해 포트를 무작위로 설정하고 로드밸런싱도 함께 적용했습니다.
물론 여기까지는 정말 기본적이고 서버의 운영의 안정성을 위한 모니터링과 내부결함 패턴을 적용하고 서버간 에러 전파를 막고 문제 발생시 관리자에게 디스코드로 전송하는 자동화까지 구현해야 하지만 역시 아직은 꿈과 같은 일이었습니다...
문서화는 무엇보다 중요하다
이번 프로젝트를 하면서 가장 크게 느낀 건, 기술적 실력보다 더 중요한 건 지식을 전달할 수 있는 능력이라는 것이었습니다.
깃허브 사용 방법과 같은 기반이되는 기술에서 문제가 있었는데 해당 내용에 대해 시스템화하여 문서화하지 못했던 것이 아쉬웠던 것 같고 이 뿐만 아니라 제가 구현한 모든 api나 설계 과정들을 해당 프로젝트에 대한 기반 지식이 없는 사람들도 모두 이해할 수 있을 정도로 문서화를 해야 한다는 생각을 하게 되었고 다음 프로젝트에선 제가 구현한 구조나 컨벤션이 왜 그런 구조가 되었는지를 설명 가능한 문서화를 하는 것을 목표로 하고 있습니다.
이번 13주차 프로젝트는 기술적으로 성장했다기보다 '설계하는 개발자'로서 사고하는 법을 훈련한 시간이었다고 생각합니다. 기술은 많고, 구현할 건 한도 끝도 없지만 어떤 문제를 왜 그렇게 해결했는가, 그 흐름을 설명할 수 있는가, 그 구조가 협업에 도움이 되는가를 늘 고민하는 습관이 이번 프로젝트를 통해 제 안에 자리잡게 되는 기반이되는 시간이었다고 생각합니다.
다음 프로젝트에선 지금보다 더 나은 구조를 만들고 더 쉽게 협업하고 더 안정적으로 운영할 수 있는 서비스를 만들고 싶습니다. 그리고 그 과정을 계속 기록하면서 협업에 능통한 개발자가 되기를 희망합니다.
한 주 밀릴 때마다 동기 전원에게 커피라니 벌칙(?)을 강하게 거셨네요 ㅋㅋ
프로젝트를 하며 나의 느낀 점을 정리해보는 것도 좋지만, 함께 한 다른 팀원의 회고를 보면서도 배우는 점이 많네요. 백엔드 프로젝트 정말 수고 많으셨고, 프론트도 함께 잘 해봐요!
백엔드 프로젝트 회고에 대한 댓글도 여기에 한번에 달자면, chatGPT에 지나치게 의존해서는 안되지만, 반복되는 노가다 작업에는 적절히 활용하여 시간을 단축하는 게 좋겠더군요. 예를 들면 DTO를 만든다거나, 완성된 비즈니스 로직에 대한 코드 초안을 만들어주는 정도?