공부/Javascript

[Youtube 클론코딩] 프로필 수정 기능

도리암 2022. 11. 22. 18:13

프로필 수정 기능을 추가하기위해 페이지를 추가해보자.

 

순서

1. 라우터에서 프로필 수정에 사용할 url과 컨트롤러 설정

2. 컨트롤러에서 프로필 수정에 사용할 페이지 render

3. 프로필 수정에 사용할 페이지 파일 제작

4. 프로필 수정 기능을 컨트롤러에 추가

5. URL 보호 처리

1. 라우터에서 프로필 수정에 사용할 url과 컨트롤러 설정

2. 컨트롤러에서 프로필 수정에 사용할 페이지 render

3. 프로필 수정에 사용할 페이지 파일 제작

로그인 된 유저 정보를 화면에 표시해주어야 하는데, 우리는 이미 현재 로그인 한 유저의 정보를 세션으로 가지고있다.

또한 middleware를 이용해서 세션의 정보들을 다음과 같이 res.locals에 저장해두었는데,

pug는 res.locals에 저장된 변수들에 접근할 수 있으므로 위처럼 작성해주면 된다.

 

여기서 만약 user정보가 없게 되면 오류가 발생할 수 있는데,

이를 방지하기 위해서 user정보가 없으면 빈 객체를 저장할 수 있게 || 를 이용했다는 것에 주의하자.

4. 프로필 수정 기능을 컨트롤러에 추가

먼저 form에서 POST로 데이터들을 전송하므로 postEdit에서 데이터를 받아와야 한다.

프로필 수정을 위해선 DB를 수정해야 하는데 mongoose의 findByIdAndUpdate()를 이용하면 된다.

Mongoose v6.7.2: (mongoosejs.com)

 

Mongoose v6.7.2:

Parameters: doc «Object» values for initial set [fields] «Object» optional object containing the fields that were selected in the query which returned this document. You do not need to set this parameter to ensure Mongoose handles your query projection

mongoosejs.com

이를 이용하려면 각 객체의 id값이 필요한데, DB를 살펴보면 다음과같이 _id라고 저장돼있는 것을 볼 수 있다.

즉, 현재 세션의 유저 정보에서 id를 가져오고 form에서 입력받은 데이터를 객체형태로 전달해주면 된다.

ES6의 구조분해 할당

세션의 유저 정보와 form에 입력된 데이터는 결국 req라는 하나의 변수에 내재 돼 있다.

ES6에서는 다음처럼 객체의 구조분해를 지원한다.

이를 이용해 기능을 구현해보면

이것과 같다. 그런데 한 가지 문제가 발생하는데, DB에서는 실제로 값이 변하지만,

페이지를 새로고침 해봐도 웹사이트에서는 수정된 내용이 반영되지 않는다는 것이다.

 

이는 웹페이지에서는 res.locals의 변수에 접근하고, locals 변수는 req.session의 정보를 참조하기 때문이다.

그리고 우리는 아직 req.session의 정보를 수정하지 않았다.

 

세션 정보 수정 방법

1. 수동

각 정보들을 한번 더 반복해서 써줘야 하므로 보기 좋지 않다.

2. 자동

mongoose의 문서를 읽어보면, 세번째 매개변수로 옵션을 입력받을 수 있는데 여기서 new라는 옵션이 있다.

new를 true로 넘겨주게 되면 findByIdAndUpdate() 메소드는 변경된 객체를 반환하게 된다.

따라서 이 반환된 객체를 세션으로 넘겨주면 된다.

예외 처리

우리는 현재 name(유저의 실제 이름), email, username(유저 로그인에 사용되는 닉네임), location을 수정할 수 있는데

email과 username 은 Unique Key로 모든 데이터 베이스에 한 개만 존재한다.

따라서 이미 데이터베이스에 username이나 email이 존재하면 수정할 수 없게 예외 처리가 필요하다.

 

방법 1. 각각의 요소에 대해 존재 여부 파악하기

email과 username에 대해서, 각 요소가 변경된 경우에 각각 User.exists() 메소드를 실행한다.

만약 null이 반환되면 사용하지 않았다는 의미고, 그렇지 않으면 이미 사용되었으므로 errorMessage를 반환한다.

 

그러나 이 방법은 exists를 최대 두번까지 시행하므로 최적화 측면에서 바람직하지 못하다.

방법2. $or 파라미터 사용

파라미터 배열을 만든 뒤, 배열에 하나라도 들어있다면 User에서 찾는다.

이 때 $or를 이용하면 둘 중 어느 하나라도 일치하는 객체가 있다면 가져오게 된다.

 

이 방법은 findOne 메소드를 한 번만 실행하므로 효율이 좋다.

5. URL 보호 처리

마지막으로 우리는, 로그인 돼 있지 않은 경우에 프로필 수정 화면에 접근하는 경우를 차단해주어야 한다.

세션을 확인해보면 로그인 여부를 Boolean 형태로 저장하고 있으므로 이를 이용하는 미들웨어를 만들어주자.

또, 이미 로그인 된 경우에 로그인 화면에 접근이 불가하도록 하는 미들웨어도 만들어주자.

라우터에서 접근 제어가 필요한 부분에 미들웨어를 넣어주자.

 

route() 메소드를 이용해서 처리한 부분은 all() 메소드를 사용하면 되고,

get()이나 post() 메소드를 이용해서 처리한 경우 일반 미들웨어 사용하듯이 쓰면 된다.