-> 비즈니스 레이어: 각각의 비밀번호의 유효성과 복잡도 (최소길이, 대소문자, 특수문자 등 특별한 설정 여부)
--> 외부에서 들어오는 다양한 요청을 걸러주는 역할
presentation layer를 통해서 들어오는 부분인데, 회사의 서비스적 기획이 녹아들어져서,
이곳을 통과하지 못하면,
다음 persistence layer(데이터베이스와 소통하는)로 해당 정보를 전달하지 않는.
즉 우리 회사의 비즈니스를 운영하기 위해 필요한 로직
1-3. Persistence Layer
클라이언트와 가장 멀리 떨어져 있고, 데이터베이스와 가장 맞닿아 있는.
데이터베이스와 관련된 로직을 구현
Business Layer에서 필요한 데이터 생성, 수정, 읽기 (CRUD) 관련 처리
실제로 데이터베이스에서 데이터를 저장, 수정, 읽어 들이기 --> SQL,ORM 적 어휘라던지 데이터베이스가 알아들을 수 있는 프로그래밍적 소통방식 택
02. Layered 아키텍처의 핵심 요소
2-1. 단방향 의존성
각각의 레이어는 오직 자기보다 하위에 있는 레이어에만 의존하고 있음
자신이 보고 있는 가장 가까운 주체를 건너뛰어서 소통할 수 없음!
Presentation layer --> business layer에게 의존, business layer --> persistence layer에게만 의존 (건너뛰어서 바라볼 수 없다)
Presentation layer --> Business layer를 건너뛰고 Persistence layer에 접근하는 일은 절대 일어나지 않는다
Business Layer --> presentation layer에 대해 (뒤에 있는 layer에) 완전히 독립적, persistence layer(앞에 있는 layer) 바라보는, 의존
Persistence layer --> (뒤에 있는 layer에) business layer나 presentation layer에 대해 완전히 독립적
의존성 관련해서는, 네모칸 화살표에 집중!
의존한다 = node express에서 "require하고 있다"
2-2. 관심사 분리(SOC)
각 레이어 별 역할이 구분되어 명확하다 --> 즉 역할의 중첩이 없다
Presentaion Layer - Controller Business Layer - Service Persistence Layer - Model
각각의 역할이 분리되었기에, 서로의 역할과 기능이 무엇인지 알 필요 없음.
모델이 데이터베이스와 어떻게 소통, 처리했는지 알 필요 없음.
Presentation layer : 비즈니스 로직이 전혀 구현되어 있지 않다
Business layer: 데이터베이스 관련 로직이 전혀 구현되어 있지 않다/ 데이터베이스 처리를 하기 위해서는 persistence layer의 코드를 호출해서 사용해야
요리사가 핫소스 넣었는제, 어떤 과정을 거쳐서 요리를 하는지 중간 직원은 전혀 알 수 없습니다. 직원은 요리사에게 요리를 받아서, 고객에게 전달만 하면 됨 과정을 거친 결과물인 요리만을 전달하기 때문입니다. 중간과정의 절차에 대한 관심은 끄면 됨. 직원은, 서비스 단에서, 고객의 요청을 잘 전달하고, 응답을 전달하는, 자신이 맡은 역할과 기능에만 집중하면 됨
서로 로직이 완전히 분리, 각각의 기능이 분리 --> workflow
controller, service, Dao (model; Data Access Object, 데이터에 접근할 수 있는 객체, mysql/rdbms와 주고받을 수 있는) *회사의 naming convention에 따라
app.js --> <회원가입> 기능 빼낼 때 user controller, user service, user Dao
2-3. Layered 아키텍처 적용시 장점
확장성 : 각 레이어가 서로 독립적이고 역할이 분명하므로서로에게 끼치는 영향을 최소화하면서 확장하거나 수정
가독성 :레이어가 완벽하게 분리되어 있고 역할이 명확하므로 가독성도 높아지니다. 코드의 구조를 파악하기가 쉬울 뿐만 아니라 각 레이어의 코드의 목적이 명확하고 범위도 확실하기 때문 (코드는 쓰여지는 시간보다, 읽어지는 시간이 길다!)
재사용성 : 레이어가 독립적이므로 a 라는 기능을 하는 a layer를 b와 c 기능을 하는 b,c layer에서도 차용 가능 (역할 중복되지 않으니까) / business layer는 여러 다른 presentation layer에 적용될 수 / Express 기반의 API 엔드포인트에 적용된 business layer가 다른 프레임워크를 사용한 엔드포인트에 사용될 수도 있습니다.
테스트 가능성 : 이미 명확한 역할에 이해 나뉘어 있으므로 각 레이어를 테스트하는 코드도 레이어가 나뉘는 것처럼 명확하게 나눌 수 있으며, 복잡한 로직이 아니라 명확하고 범위가 확실한 기능만을 테스트하기 쉽다.명확한 범위 내에 내가 의도한 작은 레고, 부품 조각들만 테스트해서, 그 테스트 조각들을 모아서, 적용 가능/ 또한 레이어들 자체가 다른 레이어에서 사용하는 (맞물리는) 구조이므로, 테스트에서 호출해서 테스트해보기가 쉽다.
03. Layered 아키텍처 적용하기
single-layered pattern 에서 multi-layered pattern으로 어떻게 변환되는지 과정에 집중!
코드 심화 과정 1. app.use(cors()); app.use(morgan('dev')); app.use(express.json()); --> 서버를 돌릴 때 항시 발동할 수 있게, 'middleware'로서 넣음
2. app.listen (server.listen 를 포함하는 다양한 종합적 기능) vs server.listen (listening만 하는 기능 국한)
app.js 가 "cors, morgan, express, routes "라는 하부모델들과
어떻게 연쇄적으로 연결됐는지 확인해보자!
1. Controller(Presentation Layer)
API의 endpoint들을 정의하고
전송된 HTTP 요청(request)들을 읽어 들이는 로직을 구현
비즈니스 로직으로 흘러들어가야할 데이터들이 올바른 형태를 띄고 있는지 선검증 작업
아래는 다양한 예시 중, 서버에서 원하는 특정 데이터의 ‘Key’ 값이 요청시 전해지지 않았을 때 발생하는 Key Error 를 사전에 에러처리로 검열한 모습
- 아래 코드 분석
userService라는 변수 안에,Service에서 불러와야 할 모듈에 대한 require문 (=import)
try, catch : signup 회원가입 관련 필요한controller 안의 기능 - 안에 name, email, passowr, profileimage 가 구조분해 할당돼서, req의 body 안에 들어있음.
에러핸들링 if
위에서 async 문을 받아왔으니, 밑에서 await 문으로 async- await 비동기 처리 : userService 안에 있는 signup이라는 또다른 함수를 불러와서, 데이터를 넣는 작업
** controller에서 service단을 require하면서 바라보고 , service에게 무언가를 계속 원하고 있다 --> service에서 signup 모듈을 받아와서, controller는 구조분해 할당한 4가지 변수의 인자를 넣어주는 모습 그림 참고흐름 참고 ** controller --> 또다시 스스로 모듈이 돼서, 외부의 호출도 기다릴 수 있는 상태가 돼야 함 (route 가 기능하기에)
다음은 실제 비즈니스 규칙(Rule)과 로직(Logic)들이 접목되는 Service 레이어의 코드 입니다. 어플리케이션을 직접 다루는 운영자 입장에서 기획한 비즈니스 모델들이 접목 되어야 합니다. 아래는 비밀번호의 조합이 특정 형태를 띄어야 하는 규칙을 위해 정규표현식을 도입한 모습입니다.
App → Router → Controller → Service → Models순으로 갈수록 데이터베이스의 접근에 가까워진다.
각각의 파일에서 export 한 module들을 어느 파일들이 require하고 있는지 그 연결흐름을 잘 살펴보면,
상위 레이어에서 오직 하위 레이어로만 의존하는 방향성의 특성
실제 Express를 이용한 서버 웹 개발시, Layered Pattern을 이루는 기본 구조
app.js | server.js: Express App 으로 서버를 여는 로직입니다. 그리고 Express App 인스턴스를 만들고, 필요한 미들웨어를 붙이는 로직입니다. 경우에 따라서 app.js 와 server.js 에 각기 다른 기능을 유도하여 두가지 파일 모두를 유지할 수도 있고, 둘의 기능을 한데 모아서 한 파일만을 유지할 수도 있습니다. 개발자의 의도에 맞게 파생되어질 수 있는 다양한 경우의 수에 유의하며 코드를 작성해주시기 바랍니다.
routes: 라우팅(엔드 포인트 나누기) 로직을 담당합니다.
controllers: 엔드포인트에 해당하는 함수 로직 - http 요청에 따른 에러 핸들링, service 로직에서 데이터를 받아와서 응답으로 내보내는 로직입니다.
services: controller 에서 넘겨받은 인자로 다양한 알고리즘(필터, 정렬 등..)을 처리해서 데이터에 접근하는 로직입니다.
models: 데이터베이스에 접근하기 위한 모델(DAO)이 정의되어 있는 폴더
아래 모듈은 의존성 없이 다양한 레이어에서 사용될 수 있지만 반복되는 로직이기에 분리해 놓은 폴더 입니다.
middlewares: 컨트롤러에 닿기 전에 반복되는 로직을 모듈화 해 놓는 폴더입니다. (ex. validateToken - 인증 / 인가)
utils: 의존성 없이 모든 레이어에서 공통적으로 자주 사용되는 로직을 모듈화 해 놓는 폴더입니다. (ex. errorGenerator)
.env: 프로젝트 내에서 사용할 환경 변수를 선언해 놓는 곳이며 샘플 양식은 .env.sample에 따로 기입하여 공유합니다.
node_modules: 노드 패키지 모듈
.gitignore: 위의 두 모듈을 깃이 관리하지 않도록
package.json: 노드 모듈을 관리하는 파일
4. Summary
Layered pattern은 기본적으로 세가지 layer(Presentation, Business, Persistence) 구조로 구성
Layered pattern은 ‘단방향 의존성’과 ‘관심사 분리’ 라는 두가지 핵심 원리에 기반한 개념
각각의 Layer들은 외부 요청을 처리하는 Controller - Service - Model (상위 → 하위)의 의 방향으로 의존성이 부여되어 있다
Assignment 1 | Project Layered pattern 적용
1. 과제 안내
지금까지 app.js 단일 파일로 만든 API에 Layered Pattern 적용하여 재구성합니다.
app.js에 모두 모여 있던 소스 코드를 각 기능별로 routes, controllers, services, models 모듈화하여 분리해주세요.
routes : endpoint를 분리해서 해당 endpoint에 집중합니다. endpoint와 controller를 연결합니다.
controllers : request와 response에 대한 처리만을 담당합니다. service layer를 호출하고, response를 반환해야 합니다.
services : 필요한 비즈니스 로직을 담당한 후, model layer를 호출해야하고, controller layer로 결과를 반환해야 합니다.
models : 데이터베이스와의 통신만을 담당한 후, service layer로 결과를 반환해야 합니다.