[First Project] 로그인한 회원의 마이페이지 불러오기(GET myPage)

hailey·2020년 11월 7일
1

FirstProject

목록 보기
8/14
post-thumbnail

1. 마이페이지 flow 정리

1) 로그인한 사용자가 마이페이지 버튼을 누르면
2) 사용자와 일치하는 정보를 데이터베이스에서 찾아
3) 클라이언트로 넘겨준다.

2. API

200: OK

{    
      "bootcampname": "bootcampname",    
      "useremail": "useremail",    
      "githublink": "githublink" , 
      "level": "level" , 
      "price": "price" , 
      "recommend": "recommend",
      "curriculum" : "curriculum", 
      "comment" : "comment",
      "githubId" : "githubId"
}

404: Not Found

err

탈퇴가 혼란이었다면 마이페이지는 대혼란...!
API 세부사항을 꼼꼼히 기록해두지 못한 게 정말 많은 영향을 미치고 있다.
마이페이지에서는 사용자의 이메일, 깃허브 아이디, 그동안 작성한 리뷰까지 총 3가지의 정보를 전달해야 한다. 그리고 위의 200 응답은 그 모든 정보를 하나의 객체로 담고 있다(useremail, githubId를 제외한 내용이 리뷰카드에 포함되는 내용이다). 내가 헷갈렸던 점을 정리해보면
1) 깃북에는 GET mypage, GET userReview, GET userInfo가 있고
2) 깃허브의 이슈카드는 위의 세 가지 API를 하나의 이슈로 묶고 있다.
3) 하지만 마이페이지에 표시되는 전체 정보가 userReview와 userInfo로 나뉘는데 GET mypage는 대체 뭘 get해온다는 걸까?
4) API의 res를 보면 그냥 데이터베이스의 모든 정보를 모아서 한 번에 주는 것 같은데?
5) (최종 API에서는 삭제했지만)그리고 쿠키는 어느 맥락에서 갑자기 사용되는걸까? 세션에서 찾은 정보를 Set-cookie로 넣어주나? 이런 식으로?

 res
        .writeHead(200, {
          'Set-cookie' [
            'bootcampname=bootcampname',
            'useremail=useremail',
            'githublink=githublink',
            'level=level',
            'price=price',
            'recommend=recommend',
            'curriculum=curriculum',
            'comment=comment',
            'githubId=githubId',
          ],
        })

였다.
혼자서는 도저히 답이 안 나올 것 같아 대략적인 코드의 틀을 잡아 PR한 뒤에 팀원들에게 메시지를 남겼다.


3. 코드 작업 과정

*1차 코드

const { users } = require('../../models');

module.exports = {
  get: (req, res) => {
    const session = req.session;

    if (session.userId) {
      users.findOne({
        where: {
          id: session.userId,
        },
      });
      res.status(200).json(data);
    } else {
      res.status(404).send('err');
    }
  },
};

SR문서가 혼란을 일으켰던 건 GET userReview와 GET userInfo를 미처 삭제하지 못했기 때문이었다. 처음에는 GET userReview와 GET userInfo를 구성하여 각각 정보를 가져오기로 했다가, GET mypage라는 하나의 API로 모든 정보를 전달해주고 클라이언트에서 필요한 정보를 골라 렌더링하는 방식이 더 효율적이라는 생각이 들어 추후 수정했던 것이다. 그렇다면 쿠키 역시 사용하지 않아도 된다.
이런저런 얘기를 다 했었기에 기억속에 그 모든 것이 뭉뚱그려 들어있어 더욱 머리가 복잡했다. 역시 소통이 중요하다는 걸 매번 체험한다.
정리하면, GET mypage는 서버에서 session에 저장된 특정 사용자의 전체 정보를 클라이언트로 넘겨주는 API다. 이제 1차 코드에서 사용자의 정보를 어떤 형식으로 보낼지만 결정해주면 된다.

*2차 코드
모든 정보를 불러와야하므로 메소드를 findAll로 바꾸었다. 정보의 형태는 다른 API인 GET reviews와 통일시켰다.

const { users } = require('../../models');

module.exports = {
  get: (req, res) => {
    const session = req.session;

    if (session.userId) {
      users.findAll({
        where: {
          id: session.userId,
        },
        include: [
          {
            model: users,
            as: 'useremail',
            where: {
              active: true,
            },
            attributes: ['email'],
          },
          {
            model: bootcamp_list,
            as: 'bootcampname',
            attributes: ['name'],
          },
        ],
      });
      res.status(200).json(data);
    } else {
      res.status(404).send('err');
    }
  },
};

3차 코드

const { users } = require('../../models');

module.exports = {
  get: (req, res) => {
    const session = req.session;

    if (session.userId) {
      users.findAll({
        where: {
          id: session.userId,
        },
        include: [
          {
            model: bootcamp_list,
            as: 'bootcampname',
            attributes: ['name'],
          },
          {
            model: reviews,
            as: 'reviews',
          },
        ],
      });
      res.status(200).json(data);
    } else {
      res.status(404).send('err');
    }
  },
};

사용자가 작성한 리뷰카드를 불러오는 코드가 없었다(^^..). 또 도입부를 보면 users에서 데이터를 찾는다고 선언해줬기 때문에 바로 아래에서 다시 users 모델을 indclude에 넣을 필요가 없으므로 삭제했다. include는 일종의 join개념이다.

클라이언트 팀원의 코드 리뷰
계속 헤매는 나를 보고 클라이언트를 맡고 있는 팀장이 코드리뷰를 해주었다. 세세하게(하지만 굉장히 중요한) 놓치는 부분도 많았고, 클라이언트단을 고려하지 않아 결과적으로 통신에 대한 우려도 생기게 됐다.

1) users 테이블의 email, githubId, password를 모두 받아오려면 아래와 같이 코드를 작성하거나, right outer join을 사용할 수도 있다. 모든 회원 정보가 reviews 테이블과 join되기 때문이다.

const { reviews } = require('../../models');
const { users } = require('../../models');
const { bootcamp_list } = require('../../models');
module.exports = {
  get: (req, res) => {
    const session = req.session;
    console.log('session', req.session.userid);
    if (session.userid) {
      reviews
        .findAll({
          //해당 user가 작성한 review들만 가져와야하므로 users 테이블에서 데이터를 가져오는게 아니라 reviews 테이블에서 데이터를 가져와야합니다.
          where: {
            users_id: session.userid, //Reviews 테이블 에서는 req.sesssion.userid가 들어있는 column명이 users_id 이다. (그냥 id가 아닙니다)
          },
          include: [
            //이제 나머지 users테이블과 bootcamp_list 테이블에서도 데이터를 받아와야하므로 외래키 연결
            {
              model: bootcamp_list,
              as: 'bootcampname',
              attributes: ['name'],
            },
            {
              model: users,
              right: true, //right outer join
              as: 'useremail',
            },
          ],
        })
        .then((data) => {
          console.log(data);
          res.status(200).json(data); //then을 써서 비동기로 처리해 주어야 합니다. then없이 기존의 방식대로 하면 그냥 이렇게 하면 data는 undefined로 뜹니다.
        })
        .catch((err) => {
          console.log('에러');
          res.status(404).send('err');
        });
    } else {
      res.status(401).send('Unauthorized');
    }
  },
};

하지만 리뷰카드를 하나도 쓰지 않은 사용자에 대해서는

 reviews
        .findAll({
          //해당 user가 작성한 review들만 가져와야하므로 users 테이블에서 데이터를 가져오는게 아니라 reviews 테이블에서 데이터를 가져와야합니다.

         where: {
            users_id: session.userid, //Reviews 테이블 에서는 req.sesssion.userid가 들어있는 column명이 users_id 이다. (그냥 id가 아닙니다)
          },

이 조건을 만족하는 사람이 없기 때문에 client는 빈 배열을 응답으로 받게 된다. 그러나 Mypage에서는 리뷰를 작성하지 않은 사용자에 대해서도

useremail: {email: "jhjyj5414@naver.com",
githubId: "Gracechung-sw",
password: "guswjd5414"}
이 세가지 정보는 받아야만 한다.

마이페이지의 개인정보란에 랜더링 되어야 하고, 개인정보수정을 누르면 리다이렉트 되는 컴포넌트에게도 props로 전달이 되어야 하기 때문이다.
이를 해결하기 위해서 아에 처음에 다 받아온 뒤에 회원의 세션 번호(userid)에 해당하는 것만 걸러주는 것으로 필터링을 하면
client로 리뷰카드를 작성하지 않은 user 정보에 대해서도 응답을 받을 수 있다. (review카드를 작성하지 않았기 때문에 null로 출력)

그런데 null 값을 랜더링 하라고 하면 또 에러가 발생한다. 결국 Mypage라는 같은 페이지 내에서 사용하는 API이지만 그 페이지에 있는 다른 컴포넌트에서 필요한 데이터가 다르기 때문에, get Mypage라는 하나의 API를 사용하는 것보다 get Userinfo, get Userreview 이런 두 개의 API로 따로 사용하는게 좋을 것이다.

여기까지가 제안받은 내용이었고, 최대한 효율적으로 문제를 해결하기 위한 다시 고민하던 팀장님이 해답을 찾아냈다!

최종 코드

늘 그렇듯 에러 없이 완성된 코드는 볼 땐 끄덕끄덕하지만, 그 과정에 닿기까지 머릿속이 꼬여버리는 일이 많다.
아래의 코드는 테이블 2개를 거쳐가니까 첫번째 테이블(users)의 .then 안에서 두번째 테이블(reviews)을 넣고 두번째 테이블의 .then 안에서 분기하는 식으로 작성했다.

const { users } = require('../../models');
const { reviews } = require('../../models');
const { bootcamp_list } = require('../../models');

module.exports = {
  get: (req, res) => {
    const session = req.session;
    const users_session_id = session.userid;
    let result;
    users
      .findOne({
        where: {
          id: users_session_id,
        },
      })
      .then((user) => {
        if (!user) {
          res.staatus(401).send('unvalid name');
        } else {
          result = user;
          reviews
            .findAll({
              where: { users_id: users_session_id, active: true },
              include: [
                {
                  model: users,
                  as: 'useremail',
                  where: {
                    active: true,
                  },
                  attributes: ['email'],
                },
                {
                  model: bootcamp_list,
                  as: 'bootcampname',
                  attributes: ['name'],
                },
              ],
            })
            .then((data) => {
              if (data.length === 0) {
                return res.status(200).json([result]);
              } else {
                return res.status(200).json([result, data]);
              }
            })
            .catch((err) => {
              res.status(500).send('err');
            });
        }
      })
      .catch((err) => {
        res.status(500).send('err');
      });
  },
};

3-1. 참고한 고마운 글들

1) res.render
https://hashcode.co.kr/questions/8783/nodejs-%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%95%9C-%EC%9C%A0%EC%A0%80-%EA%B2%8C%EC%8B%9C%EA%B8%80-%EC%96%BB%EC%96%B4%EC%98%A4%EB%8A%94-%EB%B0%A9%EB%B2%95

2-1) 쿠키 사용하기
http://blog.naver.com/PostView.nhn?blogId=pjok1122&logNo=221547826216

2-2) 쿠키 사용하기(생활코딩)
https://opentutorials.org/module/3630/21740

2-3) 쿠키와 마이페이지 로직
https://bubobubo003.tistory.com/43

4. 마이페이지 구현 회고

1) 한 번 머릿속이 꼬이기 시작하니 출발점으로 되돌아가는게, 가장 중요한게 무엇인지 파악하는게 힘들어졌다.
2) 팀 프로젝트라는 생각에 마음이 급해져서 안해야 할 실수를 많이 했다. 함께 논의해야 할 중대한 문제가 아니라면 하루를 더 쓰더라도 완성도 높은 코드를 작성하는 게 좋다.

다음에는
1. 서버단에서의 테스트만이 아니라, 클라이언트와 같이 구동해보면서 코드를 테스트해보자.
2. 제발 침착하자.





➤계속 공부하고 있습니다. 더 나은 의견과 질문이 있으시다면 언제든, 어떤 경로로든 이야기해주세요.

profile
옳고 그름을 고민합니다

0개의 댓글