Wecode - Project 2 (부트캠프)/세션

Project 1 : 회원가입 기능 layered pattern (소헌 멘토님) 총정리

JBS 12 2023. 10. 4. 19:35

layered pattern 하는 방식이 멘토님마다, 동기마다 달라서, 

 

project 1 복사 붙여놓기
복사본은 깃 반영 안 됨

ls- al 해서  .git 떠도 반영 안 된다.
ls-al 해서 나오는 건 git 삭제 

remote 연결 안 될 것

단방향에 따라서, 각 파일의 앞단에서 export로 내보낸거를 require를 가져온다. 

 

 

 index.js 는 향후 확장성을 고려하여 생성될 수 있는 다양한 Router(예시: userRouter, productRouter 등)들을 한 곳에 모아 관리하는 역할

 

1. app.js 외에 나머지를 모두 넣을 scr 폴더부터 만든다 = source 폴더

1-1. 그 안에 파일로 만든다--> src 폴더 안에 router/ controller/ services /models 

1-2. router/  controller/ services/ models 안에 각각의 파일 만들어주기 

 

함수명은 signUp -> signupcontroller

existingUser -> existingUserDao

 

 

2. router 파일과 app.js 연결이 가장 먼저.

router가 client에 가장 가까이 있으니 

 

2-1. app.js에 router 연결 

const {routers} = require('./src/routers') 

 

2-2. 어떤 함수를 실행시킬건지 코드를 router에 연결 

 

라우팅을 해주던 코드는 모두 userRouter로

app.post ("/users/signup", 

app.get('/users',

app.post("/users/login",

app.delete("/users", 

app.put("/users/1",  

 

- app.js 에는 처음에 여는 것만 있는 상태 

 

2-3. router안의 index.js 에 모든 router를 한 데 모아서 묶어줌 

url의 앞단으로 크게 대분류를 한다. : users/ thread/ comment 

그 뒤의 url로 소분류 -> 어떤 함수로 가져갈지 : signup/ login 

 

index.js가 가장 첫번째로 모아주는 역할 

2-3-1. userRouter를 require로 가져오고, threadRouter를 require로 가져오고. 

2-3-2. url 가장 앞에 user가 붙어있으면  userRouter로 보내

앞에 thread가 붙어있으면 threadRouter로 보내  --> 대분기를 

2-4. userRouter 에서는 users뒤에 signup이 추가로 붙어있으면, 다른 함수를 호출해

userRouter.post ("/signup", () => {}) 

 

router의 기능: url를 분기처리 

postman에서 확인 가능 

 

1) index.js에서는 '/user'로 들어오면, userRouter로 보내

2) userRouter.js에서는 signup, login, hello 각각의 함수를 처리

3) postman에 "/signup"로 찍으면, userRouter.js console에 찍은 "signup Router connected"이 터미널에 나오고

 

3. signup함수가 붙어야 한다 = router가 연결을 해줬으니, 그 다음에 controller에 있는 함수를 불러와야 함 

3-1. app.js 에서 req를 받는 부분까지 (req를 다루는 부분만) controller로 빼기 --> usercontroller

3-2. controller에 signup 함수를 만들기, 인자는 req& res 모두 받아주기 (client과 가장 접점에서 req, res를 다루는 지점이니)

3-3. app.js에서 회원가입 함수 가장 위의 req(1), 아래의 res 함수(2) controller로 옮기기 

(중간의 if, 서비스 관련 코드는 옮기지 않음)

error를 다루는 함수(3)도 res에 해당되기에, 옮기기 

복잡하면, service 함수엔 연결만 하고, controller 함수안에 다 넣어도 됨 

 

3-4. Controller에서 선언한 signup 함수를  export --> 다른 파일에서도 쓸 수 있도록,

Router에서 signup 함수를 사용하기 위해 import --> require로 

 

userRouter에 쓰고 있는 signup함수가 선언이 안 됐는데 어디에 있냐, 
userController 에 있다. 
그럼, userController에서 signup함수를 가져와야 한다.

근데, 먼저 userController에서 먼저 signup함수를 module로 빼내야 한다. 그래야 밖에서도 signup함수를 쓸 수 있으니

 

module.exports = { signup} --> userController.js 하단 

const {signUp} = require('../ controllers/userController') --> userRouter.js 상단 

보통 export는 그 파일에서 쓰인 함수이름이다. 
이 파일에서 쓰인 것을 다른 곳에서 쓰이게 내보낸다는 것이니. 

require 경로는: 

../ controllers/userController

지금 userRouter.js 이니까 .. 로 router로 하나 밖에 나와서 

controller 찾아가서, userController.js로 

3-5. 잘 연결됐는지 확인하는 법 

console.log('controller connected') --> controller에 찍어서 터미널에서 확인하는 문구 

postman에 router의 url '/signup'을 적고, post하면 서버가 연결된 terminal에 찍힌다

 

포인트: controller와 router를 연결 시킨다.

 

cf. node에서 index.js 가 없으면 폴더 한번에 불러오는 게 안 됨.

--> 각 router/ express/ controller 와 같은 폴더 안에, 각각 index.js가 있는 이유.

      ex. const express = require('express') 

      ex. const userController = require ('./userController')

 

router에서 controller에서 userController.js안의 파일을 통째로 불러와서 할당하는데 

너무 많아지니, controllers라는 폴더를 불러와서 통째로 쓰기 위해 index.js에서 모아주는 것.

 

--> index.js가 폴더를 패키지화 함 

 

3-6. userController에서 userService를 연결 

await userService.signUp(email, password) 

; controller가 service 로직으로 email, password를 보내는 것

 

controller는 내용은 모름, service로 보냄  

 

 

3-7.바로 service와 controller가 연결되지 않음 ; import, export해줘야 함 

 

3-7-1. signup 함수 export

- userService.js 에 signup 함수도 사용될 수 있게,  module.exports로 보내줘야 함 

- index.js 안에도 userService를 require로 가져온 뒤, module.exports로 내보내줘야 함 

 

3-7-2. const signUp =async(email, password) => {     --> userService.js에서 import 

3-7-3. const {userService} = require ('../services') --> userController.js에서 import (services는 패키지화)

 

 

4. 그러면, service에서는 곧바로 email, password를 controller에서 받아옴 

4-1. userService에서 email, password를 userController로 부터 받음

4-2. email. password 검토를 함 

 

4-3. sql문은 userDao(model)로 보내기

select 문 -> sql문

4-3-1 userDao로 가서, const getUseInfoByEmail 함수를 만들어준다 --> email을 받아오는 sql문 

4-3-2. userService.js에서는, userDao로 보내는 코드 

service는 'getUserInfoByEmail'함수가 알수 없지만, 일단 email 주고 data 갖고 오라고 시키는

Dao는 그 email를 받아서, return 

 

4-4-1. userDao.js 에 const {AppDataSource} =require('./dataSource') 

AppDataSource = datasource

4-4-2. models 안의  index.js , dataSource.js 만들기

index.js 

dataSource.js

4-4-3. userService.js에서 userDao를 연결해줘야 함 (import)

 

userDao.getUserInfoByEmail (userDao의 getUserInfoByEmail 함수를 가져온다)

'SELECT id, email FROM users WHERE email =  '${email}'; ' 이 코드를 userDao.js에 분리를 시켜놓고, 

userService.js에서는 이 코드를 가져다가 쓰기만 하는 --> 이런 식으로 역할을 위임 시키는 것. 

 

4-5. 남은 sql도 userDao.js로 내보내줘야 함 (insert into =sql문) 

insert into~ 부터 dao로 빼고, 

dao에는 const createUser로 함수명 만들어주고, 저장하는 'password, email, name'을 async로 .

 

const createUser= async(email, password, name) 

 

createUser 함수도 Dao에서 export.module 써주고,

 

sql문을 dao가 호출하는. Service에 있는 newUser 는 직접 email, password를 데이터베이스에 저장하는게 아니라,

userDao의 createUser함수로 저장한다. 

userData 함수를 newUser로 바꿈

 

 

app.js에서 sql문을 찾아라 -> models로 다 빼기 

즉, 같은 signup 함수를 3개로 쪼개는 것 (controller, service, model)

 

가장 먼저 실행되는 거 router > controller > service > dao 순서

 

router에서 /signup 이라는 url로 누가 접속했을 때, 우리가 호출하는 함수는 userController 라는 파일에 있는 signup 함수

-> userController라는 파일에 가면, signup 함수 실행 될거고, 쭉 내려가다가, await userService.signup을 호출시키니, 

-> userService로 넘어감. userService에 있는 signup 함수가 실행이 되고, 쭉 내려가서 실행 되다가, userDao에 있는 getUserInfoByEmail함수를 호출하는 코드를 만들음. 

-> userDao 파일로 넘어가서, getUserInfoByEmail함수를 실행 시킴. 위에서부터 실행이 되고, select 문하고, 이때하는 게 "첫번째 return". [여태, 함수를 반환한 적이 없고, 다른 함수를 호출, 호출해서 넘어가기만 했음] 첫번째return을 만났으니까, 이 return된 userData가, 

Dao를 호출한 Service에 가서, 

-> userService로 가서, return된 userData가, userService의 (Dao를 호출한) exisitingUser에 할당이 됨. (나를 부른 곳으로 감) 그런 다음 밑으로 쭉 내려가다가, "두번째 return"을 만남.

service layer에 있는 함수는 어디로 return 되는가?

controller! 

거꾸로! 나를 호출한 곳으로 return돼서 가야죠! 

-> userController에서, userService를 호출한 부분까지 정상 진행이 됨. 그런다음, console.log로 출력, 

그 다음, userCreated라는 response로 return이 간다. controller에 있는 response는 곧장 어디로 가나? client에게 곧장 감! 

router는 처음 들어온 요청을 분기처리만 해주고, return은 굳이 필요 없음.


즉,

1> 2> 3> 4순서로 호출되는데,

return은 4>3>2>1 거꾸로 된다.!

 

(return한 값은 거꾸로, 이전 순서로 감)

 

 

dao에서 userData 함수의 return값을 service의 existingUser로 보내줌 

service 안의 return 값은 controller의 userService를 호출한 에게


[코드의 함수식 이해]

userService에 있는 

const [existingUser] = await ~~ ? 

userDao에서
select 문을 하면 userData가 배열로 오는데, // (userService의 exisitingUser라는 변수에 배열이 담김!)

userData가 userService로 가면,
exisitngUser는 어짜피 1명 밖에 없을텐,, 무조건 배열의 첫번째 요소에 접근하게 되겠죠?

배열을, 변수를 이렇게 앞뒤로 싸면  // const [existingUser] 
첫번째 값을 해당 변수에 곧바로 할당함 --> javascript 문법. 

[코드의 함수식 이해]

 

javascript 최신 문법

function 써도 되는데, 결함이 많아서

const로 바뀜


[코드의 함수식 이해]

 

(email, password) --> 함수 처음 선언할 때 들어오는 parameter

 

-> AppDataSource로 (email, password)를 받을 거다. 

 

-->Dao에 있는 createUser를 호출할 때, 순서 잘 지켜서 보내줘야 함. // (email, password)를

(passoword, email)로 하면, 순서 바뀌어서 들어감 


console.log로 출력된 거로 확인해보면

 

1. controller 뜸 

2. service 뜸 (함수 호출돼서)

3. dao 갔다가, 다시 return 되면서 그제서야 

4. controller 로 가서 

controller 파일 안에 있음에도 불구하고, 위에서부터 순차적으로 service, dao 호출하는 코드 다 진행되고 나서,  

await userService.signUp 이 실행 됨 (기다리고 있던 게 돌아오면서 return할 때 실행됨)

 

 

즉,  호출과 return의 흐름

 

router - controller - service - model dao (호출호출 호출) 

- service - controller -client (return return return) 


이렇게 역할을 명확하게 분리해놓으면, 

 

 

sql에서 문제나면, 

model 가서 확인 하면 되고, 

 

통신에러가 나서 안 되면, controller 파일로 가서 디버깅하면 되고, 

 

404 떠서 

router 경로 이상하다 -> router 파일 가서 이상한 건 없는지 

 

유저 비밀번호 에러났다 -> service 가면 됨

 

 


추가로, dataSource도 app.js에 안 있고, 

datSource.js로 빠져 있음

 

database 관련된 설정이니,

models 밑에 넣고. 

 

 

app.js에는 다른 건 없고,

서버 키고 // const server= http. createServer(app) const start=async()=> { 

데이터베이스 연결 코드만. //const {AppDataSource} = require('./src/models/dataSource');


api에 따라서 service가 아예 없는 api도 있음 (예외처리가 없는)

 

 

updateUser는 예외처리할 게 없어서 service 없고, 

try - catch도 controller에 하고. 

 

위와 같이, service의 역할이 dao를 호출하는 역할만 하기도 함 

그래도 꼭 있어줘야 함

순차적으로 불러줘야하지, 하는 일이 없다고 건너뛰어서 불러주면 안 됨.


index.js는 일단 패키지로 다 만들고 시작하고,

>파일 안에 각각의 함수를 만든 후,

 

>해당하는 함수를 밖에서 쓸 수 있게끔, 파일에서 밖으로 내보내기 (module.exports) 

함수를 export

>index.js에서는 파일을 불러오는 것 (파일만)

userService라는 파일 -> export

파일 안에서 export하는 건 -> 함수

index.js에서 export하는건 -> 파일 

 

함수를 export하는 이유는 함수를 다른 파일에서 쓰기 위해 

파일을 export하는 이유는 함수를 다른 패키지/폴더에서 쓰기 위해  (다른 폴더에서, 다른 폴더 안에 있는 파일을 쓰기 위해)

module = 파일 
모듈을 export할 거야
함수를 객체의 형태로 담아서 내보낼 것 // module.exports = { "signUp":signUp } 

다른 파일에서 이 파일(module) 을 다운로드 받는 게 사실은, //const userService = require('./service/userService.js')
export한 이 객체 다운 받는 것
console.log 찍어서 확인하는 방법 -> node app.js 실행시키면 됨 
userService 파일(객체)안에 signUp 함수를 가져올 거야 // userService.signUp 
app.post("/users/signUp", userService.signUp)
node는 그냥 폴더만 import하는게 안 되기 때문에, 
패키지로 꼭 만들어줘야 해서 index.js가 꼭 필요함 

npm install로 설치하는 모든 것들은
node modules안에 파일 안에 들어가는데, (npm install http, express, index, typeorm) 
이걸 쓸 수 있는 이유는, typeorm이 index로 모아서 다른 것들을 export 해주고 있기 때문에. 

우리가 쓰는 모든 라이브러리는 모두 패키지로 만들어져 있고,
우리도 우리가 만든 함수들을 다른 파일에서 쓸 수 있게끔
패키지화해서 분리 하는 것. 

 

(한두번 들을 땐 이해 안 되던게, 

구글링, 팀원과 말로 스터디 하고 다시 영상 보니 

이 모든 게 합쳐져서 이해가 된다)