Router

정적 경로와 동적 경로(파라미터)를 같은 도메인에서 사용할 때 순서에 따라 충돌이 발생할 수 있다.

동적 경로가 정적 경로보다 먼저 선언되어 있을 경우 정적 경로(user/check-email 등)의 경로 값이 동적 경로의 파라미터로 인식되는 문제가 발생한다.

https://docs.nestjs.com/controllers#route-parameters

CQRS

Command 와 Query 를 분리하여 성능, 확장성, 보안성을 높이는 아키텍처 패턴.

서비스를 컨트롤러에서 떼어버리고, Command 혹은 Query 를 거쳐 비즈니스 로직이 실행되도록 구현하였다. 이 때 서비스는 비즈니스 로직을 캡슐화 하는 역할을 수행한다.

다만 읽기와 쓰기 시 사용하는 모델을 분리하는 이점이 현재 프로젝트에는 크게 작용하지 않았다.(애초에 코드만 분리하고 같은 모델을 사용했다)

오히려 Command 와 DTO를 중복되게 작성하며 의문이 늘어났고, 비교적 단순한 도메인을 복잡하게 구현하는 결과로 이어졌다.

그나마 이벤트 발행이 간편해지는 장점이 있었으나 아직 까지는 여러모로 불필요한 분리 작업이라는 생각이 든다..

CQRS에 대해 공부하며 계층 간 분리를 위해 도메인 모델이나 DTO 객체를 여러 계층에 혼용하지 않아야 함을 알게 되었다.

그동안 DTO를 남발하며 엉망진창 개발했던 것이 떠오르며 반성하게 되었다.

<aside> 💡

쿼리 분리에 대한 고민

user 도메인은 getUserById getUserByEmail getUserByStudentId 와 같이 각각의 매개변수에 대응하는 메소드들을 가지고 있다.

당연히 세가지 메소드 모두 인증, 관리자 도메인에서 필요로 하기 때문에 쿼리로 제공해야 하는데 이를 위해 동일한 로직에 대한 쿼리를 3개나 만들어야 하는 상황이 발생했다.

대안으로 선택적 매개변수를 적용하여 하나의 쿼리에서 3가지 분기를 처리하도록 하는 방법이 떠올랐는데, 단일 책임 원칙 위반 가능성과 더불어 예외 처리가 더욱 복잡해지는 문제가 있다.

CQRS를 적용하지 않았을 때에는 단순히 컨트롤러에서 서비스로 모두 연결해주었기 때문에 이러한 고민은 없었다.

결국 전자가 더 정석적인 방법이라고 생각하여 쿼리와 핸들러를 각각 3개씩 구현했지만 중복되는 코드에 대한 찝찝함이 남아있다.

</aside>

<aside> 💡

핸들러의 반환 타입 문제

Command 혹은 Query 의 핸들러에서 execute 메소드의 반환 타입을 명시해도 버스를 통해 실행하면 any 타입으로 반환된다.

이는 핸들러를 연결해주는 CommandBus, QueryBusexecute 메소드가 제네릭 타입으로 명시되어있지 않기 때문이다.

따라서 반환 타입이 지정된 버스를 만들어 확장하거나, 버스를 사용하는 코드에서 타입을 명시하여 사용해야 한다.

모듈을 수정하여 execute<R>(query: IQuery) 와 같이 사용하는 것이 조금 더 깔끔해 보이지만, 큰 차이가 없기 때문에 const user: User[] 와 같이 직접 타입 지정하는 방법을 사용하였다.

</aside>

https://velog.io/@rxolve/NestJS-CQRS-공홈-예제-뜯어보기

https://assu10.github.io/dev/2023/04/16/nest-cqrs/

https://velog.io/@naljajm/NestJs-CQRS-관심사-분리

Authorization and Authentication

자주 헷갈리는 개념이라 복습해보았다.