nest.js 공부 - controller

Cheshire Cat·2023년 8월 7일

최근에 nest.js 를 공부하고 있다.

몇번 깔짝거리면서 백엔드를 만져본 경험은 있었지만, 조금더 정리된 형태의, 정제된 형태의 지식으로 정리해보고자 남겨본다.

이 글을 보는 누군가에게도 도움이 되었으면 좋겠다.

먼저 큰 그림부터 생각해봤으면 좋겠다.

백엔드는 무엇인가?

클라이언트에서는 서버에 요청한다.
서버는 그것에 답한다.

무엇을 주세요. 라고 하는 것을 약간 유식하게,
어떤 리소스를 요청한다고 표현한다.

주고 받는 형식은 http 프로토콜을 이용한다.
어떤 정해진 방식이 있고, 그것으로 주고받나보다 정도로 이해할 수 있겠다.

결국 클라이언트는 어떤 방법(http 프로토콜)로, 백엔드 서버에 요청하고,
백엔드 서버는 그것에 응답하는 것이다.

백엔드 입장에서 요청을 받았다고 생각해보기

요청을 한다. 클라이언트가.
문장이 여기서 끝나면 이상하다.

무엇을???? 무엇을? 무엇을이 빠진 것이다.
어떤 리소스를 요청하는가?

리소스에 대한 서술이 필요할 것이다.
시금치 주세요. 일수도 있고
시금치 중에서 잎의 크기가 작은 것들만 주세요. 일 수도 있고
시금치를 주긴 주는데, 크기가 작은것부터 큰 순서로 정렬해서 달라고 할 수도 있겠다.
아니면, 1~20번째 까지의 시금치만 달라고 할 수도 있다.

위 요청들을 처리하기 위해서는 무엇이 필요한가?

시금치 주세요
-> 일단, 시금치가 있어야 할 것이다. 그런데, 있을 수도 있고 없을 수도 있다. 그럼 없다고 해야할 것이다.

시금치 중에서 잎의 크기가 작은 것들만 주세요.
-> 요청에 잎의 크기가 작은 <- 이라는 서술이 있어야한다.

시금치를 주긴 주는데, 크기가 작은것부터 큰 순서로 정렬해서 주세요.
-> 크기가 작은것부터 큰 순서로 정렬 <- 이라는 서술이 있어야한다.

1~20번째 까지의 시금치만 주세요.
-> 시작지점부터 몇개까지의 <- 이라는 서술이 필요하다.

어쨋든, 동일한 리소스를 가르킨다. 시금치.

그럼 일반적으로 시금치 리소스를 서버에서 클라이언트로 통신하기 위해서,
'서버주소/시금치' 에 요청하라고 할 것이다.

서버라는 것이 준비해야하는 것.

어떤 통로로, 어떤 요청이 온다. 라는 말을 당신에게 전해주었다.

그럼 당신은 무엇을 해야하는가?
어떤 통로로, 어떤 요청이 올테니, 내가 거기에 응답하거나 해결해야할 준비를 해야할 것이다.

조금 더 프로그래밍적으로 접근하다면, 어떤 엔드포인트(어떤 통로)에 요청이 이렇게 올텐데,
그러면 이 콜백함수를 실행시켜라. 라고 준비 할 수 있다.

그것이 컨트롤러다.

어떤 엔드포인트(어떤 통로)에 요청이 이렇게 올텐데,
그러면 이 콜백함수를 실행시켜라. 라고 준비 할 수 있다.

그렇다면 컨트롤러의 정의는 무엇으로 할 수 있나?

특정 통로로 오는 요청에 대해 준비시켜놓은 함수라고 생각할 수 있다.
특정 통로로 오는 요청, 즉 이벤트가 발생할 때마다 해당 함수가 동작하는 것이다.
클라이언트에 응답하는 것도 컨트롤러이다.

레이어를 나누던데?

ㅇㅇ 컨트롤러 내부에서 통으로 처리할 수도 있고,
나는 서비스 레이어도 추가하고, 레포지토리 레이어도 추가할꼬야 라고 하면 해도 된다.
일반적으로 더 바람직하다.
여기서 일반적이라고 하는 것은 딱히 분리 안해도 되긴 한다.

컨트롤러 - 서비스 - 레포지토리 레이어를 나눈다면 어떻게 작성해야하나?

request에 대한 정보는 controller한테 있겠지.
여기서는 요청된 endpoint / payload 를 포함한다.

payload는 쉽게 말하면, 클라이언트가 요청한 것을 처리하기 위해서 클라이언트가 제공해줘야하는 어떤 것이라고 생각하면된다.

등본띠러왔는데요. 저는 누구입니다.(신분증을 주며)

페이로드가 없는 상황이라면?
등본띠러왔는데요.
공무원: 누구세요;;; 신분증 주셔야죠;;

이처럼 통신하다보면, 나 누군데요. 이거줘, 해야하는 상황 등이 많은데
페이로드던, 쿼리스트링이던 아무튼 어떤 수를 쓰던 요청과 같이 내가 누구다. 하는 신분증 비슷한걸 주든 해야한다.

튀밥 아저씨 찾아갈 때, 이거 튀겨주세요. 해야하듯.
먼가 주면서 해줘. 하고 요청해야할 때가 있지 않겠는가. 뭐 그런거지.

다시 정리해서

  1. 컨트롤러의 역할과 책임은 뭘까?

request 디테일을 다룸, 함께 온 payload, queryParams 등을 다룸.
그리고 그것으로 서비스 로직을 호출함.

2.그럼 서비스는 뭐하는데?

컨트롤러가 시킨걸 한다. 시금치 뜯어오라는거면 시금치 뜯어서 온다. 시금치 볶으라고 하면 시금치 볶아서 온다. 일반적으로 디비에서 데이터 받아와서 사부작 거리면서 필요한 만큼 마사지 해서, 리턴한다. 누구한테? 자기 호출한 컨트롤러겠지 뭐.

  1. 레포지토리는?

해남시금치일지 서울 시금치일지 모른다고 생각해보자.
해남시금치는 해남식으로 소통해야하고, 서울 시금치는 서울식으로 소통해야만 한다.

서비스로직이 하는짓은 동일하다. 시금치 줘. 인건데,
해남이라면 해남식 사투리를 써야하고
서울식이라면 서울식 사투리를 써야한다고 해보자.

서비스로직이 기존에는 각각 요청할 때마다 사투리 신경쓰면서 해야했다.
레포지토리 레이어를 두면, 그 사투리 신경쓰는건 걔가 하는거다.

DI는 또 뭐여

dependency injection 의존성 주입이다.
만약 특정 컨트롤러에서 서비스로직을 사용하고싶다면,
construcor에서 주입하여 사용한다.

서비스로직 함수로 만들어놓고 그거 임포트 해서 쓰면 안됩니까?
-> 된다. 다만, nest.js에서는 특정 타이밍에 공통적인 처리(AOP)를 사용할 수 있도록 지원하는데, 거기서 불리함이 생길 수 있다. 예를 들면 서비스 로직 실행할 때마다, 로그를 찍는 거 말이다. 이 경우 DI로 서비스로직을 주입했을 경우 정상동작 한다. 아니면 안할 거고.

컨트롤러 코드만 보고 구조를 파악해보면
constructor로 이런 함수를 받아와서 실행할거고,
그러니까 컨트롤러 생성시점에 이 함수를 집어넣어야 해. 라는 명세로 볼 수도 있고

nest 자체를 사용하는 입장에서는
이 서비스 로직 여기서 쓰려면 이렇게 해주시면 되요~ 라고 이해 할 수도 있다.

어떤 목수 클레스가 있는데, 컨스트럭터에서 공구를 받는다고 해보자.
그 이야기는 목수를 쓰려면 공구를 주어야한다. 라고 생각할 수 있다.
따라서 목수는 공구에 의존한다.
따라서 목수를 사용하려면 공구를 줘야한다.
뭐 이런 말장난 같은 설명을 할 수도 있겠다.

사실 함수에서의 인자와도 매우 비슷한 것이다.
걍 이 함수 실행할때, 이 인자 넣어줘야함과 딱히 다를 것 없다.

다만, 함수와 인자의 개념보다 다소 복잡해진다.

const sum = (n1,n2)=>n1+n2

이 경우라면 단순히 인자로 숫자 2개를 넣어서 결과값을 반환하는 함수로 이해할 수 있지만,
class MakeHuman {
constructor(상체, 하체){
상체.이어붙일준비()
하체.이어붙일준비()
}
}

위와 같은 경우라면,
어떤 상체를 받는다는 이야기고
어떤 하체를 받는다는 이야기가 된다.
여기서 상체와 하체는 여러가지일 수도 있다.
상체라면 이래이래 해야해. 라는 어떤 명세가 존재할 수도 있다.

따라서 어떤 상체와 하체가 올지는 모르겠지만, 아무튼 거기에 있는 메소드.
이어붙일준비라는 것을 실행한다는 의미다.

받아오는 상체 하체에 이어붙일 준비라고 하는 메소드가 있나보지. 라고 생각하면 된다.
다른의미에서는 상체라면, 이어붙일준비라는 메소드가 있나보다. 라고 볼 수도 있겠다.

저 클레스에서 더 멀 하려는진 모르겠지만, 아무튼 상체랑 하체를 받는데,
각각 받아온 상하체에서 이어붙일 준비라고 하는 함수를 실행하나보다. 라고 생각할 수 있다.

다른 말로 MakeHuman이라는 클레스를 사용하기 위해서 위와같은 명세를 만족하는 여러가지 유형의 객체를 받아서 조합성을 높일 수 있을 것이다.

그냥 그런걸 DI라고 한다. 왜 해야하는가는 객체지향에 대한 글이 되어버릴 테니 여기까지..
그냥 특정 인터페이스를 충족하는 객체를 주입해주는 것을 DI라고 한다. 정도로 이해하자.

profile
그 방향으로 가면 행복한 비상식의 세계가 있어

1개의 댓글

comment-user-thumbnail
2023년 8월 7일

좋은 정보 감사합니다

답글 달기