[유튜브 클론코딩] 6.9 Recap and User Profile

2021. 3. 12. 16:09Projects/유튜브 클론코딩

728x90
반응형

6.9 Recap and User Profile

인증(Authentication)에 대해 정리하기

📌 local 방식(username / password)

  • username과 password를 post 방식으로 전달하고
  • 우리가 설치해준 플러그인인 mongoose가 자동으로 체크를 해준다.
    • 만약 password가 맞으면 passport에게 맞다고 알리고 쿠키를 생성

 

📌깃허브 방식

 

.social-login

    button.social-login--github

        a(href=routes.gitHub)

            span

                i.fab.fa-github 

            |Continue with Github

    

    button.social-login--facebook

        span

            i.fab.fa-facebook

        |Continue with Facebook

 

  • 깃허브로 로그인하기를 누르면 /auth/github라는 라우터로 이동하게 된다.

 

globalRouter.get(routes.gitHub, githubLogin);
export const githubLogin = passport.authenticate('github');

 

  • /auth/github 경로로 가면 githubLogin 함수가 실행되도록 했는데, 그 함수는 사용자를 깃허브 웹사이트로 이동시킨다.
  • 거기서 권한 승인을 한다
  • 깃허브에서 우리에게 사용자의 정보를 보내주는데, /auth/github/callback 이라는 URL로 오게 된다
passport.use(new GithubStrategy({

    clientID: process.env.GH_ID,

    clientSecret: process.env.GH_SECRET,

    callbackURL: "http://localhost:4000/auth/github/callback"

    }, githubLoginCallback)

    

);
  • 그렇게 되면(/auth/github/callback 으로 접속하면) passport가 함수를 호출하는데 우리가 만들어준 githubLoginCallback이라는 함수를 호출한다.

 

export const githubLoginCallback = async (accessToken, refreshToken, profile, cb) => {

    const {_json : { id, avatar_url, name, email}} = profile;

    try{

        const user = await User.findOne({email});

        if(user){

            user.githubId = id;

            user.save();

            return cb(null, user);

        }

            const newUser = await User.create({

                email,

                name,

                githubId:id,

                avatarUrl : avatar_url

            });

            return cb(null, newUser);

    }catch(error){

        return cb(error);

    }

};

  • githubLoginCallback 함수는 사용자의 정보를 받아온다. (profile 같은 것)
    • 이 정보로 email로 사용자 찾기, github ID로 사용자 찾기 등 할 수 있다.
    • 이 함수의 한가지 조건은 콜백함수(cb)를 리턴해야 한다 - 에러가 있는지 유저는 있는지
      • 여기서 에러가 생기면 /login으로 redirect 된다.
globalRouter.get(

                routes.gitHubCallback,

                passport.authenticate('github', { failureRedirect: '/login' }),

                postGithubLogIn

                );
  • githubLoginCallback 함수가 user를 찾으면 passport는 "좋아!"라고 통과시키면서 postGithubLogin이 실행된다.
  • 그리고 쿠키를 만들어서 저장해줄 것이다
  • user를 찾지 못하면 /login으로 redirect 된다
export const postGithubLogIn = (req,res) => {

    res.redirect(routes.home);

};
  • postGithubLogin은 홈으로 redirect 되는 함수이다.

 

My프로필 페이지 만들기(/me)

자신의 프로필 페이지는 일반적인 userDetail처럼 동작할 필요가 없다. (req.user로 받으면 편리하다)

userDetail에서는 특정 id를 가진 사용자를 찾아야만 한다.

사용자를 찾고, try catch 써서 찾은 user를 보내고, 근데 사실 그 사용자는 로그인 되어 있는 상태이므로 자기 페이지는 그렇게 해서 찾을 필요가 없다. req.user로 받으면 되기 때문에

그러니까 `/users/${id}`가 있는 것보다 /me 로 하는게 더 좋을 것 같다.

 

📌routes.js에서 새로운 라우트 만들기

const ME = "/me";

↑ routes.js (이렇게 만들고 const routes 에다가도 me: ME 라고 써준다)

 

📌me라는 controller 만들기

export const getMe = (req, res) => {

    res.render("userDetail", {pageTitle: "User Detail", user: req.user});

}

userDetail이 하는 일과 똑같은 일을 시킬 것이다.

다른 점은 userDetail에서는 사용자를 찾는 과정이 필요할텐데 getMe에서는 user를 req.user로 전달할 것이다. 바로 지금 로그인 한 사용자 말이다.

⭐기억하자 req.user는 현재 로그인 된 사용자이다.

 getMe라는 controller는 user라는 object를 전달받은 것이다.

 

다 만들었다면 globalRouter에다가도 경로를 만들어준다.

globalRouter.get(routes.me, getMe);

 

 

📌userDetail.pug 수정하기

extends layouts/main

block content 

    .user-profile

        .user-profile__header 

            img.avatar(src=user.avatarUrl)

            h4.profile__username=user.name

 

 

📌header에서 profile 경로 수정하기

 a(href=routes.me, title="프로필") Profile

 

routes.me로 해도 아무 상관 없는 이유는,

아까 user.avatarUrl 가지고 왔던 거 기억하지?

우리의 middlewares 중에는 locals 변수인 user에 req.user를 할당했다는 것을 기억하자.

 

export const localsMiddleware = (req, res, next) => {

    res.locals.siteName = "MyTube";

    res.locals.routes =  routes;

    res.locals.user = req.user || null;

    next();

};

그래서 주소의 id를 바꿔봐도 현재 로그인한 사용자의 프로필이 뜰 것이다.

지금은 userDetail은 제 역할을 못하는 것이다. 원래는 이 id에 맞는 사용자를 찾아서 그에 대한 프로필을 보여줘야 하는 건데 제 역할을 못하고 있다.

 

지금 작동이 되기는 한다. user는 글로벌 변수로서 overriding 하고 있기 때문이다.

나중에 가서는 userDetail에서 (실제 로그인 사용자를 id로 찾아서) override 해야해

지금은 아무 id나 입력해도 로그인한 사용자의 profile이 나온다. 이건 middleware 때문이야

 

그래서 어떻게 할거냐면 이 id로 사용자를 찾고, 만약 존재하지 않으면 존재하지 않는다고 띄울 것이다.

그러니까 지금 글로벌 변수인 user를 실제 로그인한 user로 바꾸어야 하는거야

 

전역변수 user를 loggedUser로 이름 바꾸기

더 확실하게 하기 위해서 이름을 그렇게 바꿨다.

 

middlewares.js > localsMiddleware에서 res.locals.user를 res.locals.loggedUser 라고 바꾸기

export const localsMiddleware = (req, res, next) => {

    res.locals.siteName = "MyTube";

    res.locals.routes =  routes;

    res.locals.loggedUser = req.user || null;

    next();

};

 

header.pug에서도 loggedUser라고 바꾼다.

    if !loggedUser

 

이름을 loggedUser로 바꿨기 때문에, 지금 /users/아무아이디 로 접속을 해도 userDetail 페이지는 오류가 나있을 것이다.

왜냐하면 userDetail에서 아바타url 경로가 user.avatarUrl로 되어있으니까. 이건 나중에 하겠다.

 

/users/무작위 id 로 들어갔을 때 나오는 에러화면을 고치자

(임시로 이렇게 하는 것임)

 

export const userDetail = async (req,res) => {

    const {params: {id}} = req;

    try{

        const user = await User.findById(id);

        res.render("userDetail", {pageTitle: "User Detail", user});

    }catch(error){

        res.redirect(routes.home);

    }

}

↑ userController.js

userDetail 함수를 다음과 같이 고친다.

늘 그랬듯이 try catch 구문으로 바꾸고 async 와 await을 이용해서 비동기로 바꾼다.

req.params를 이용해서 주소창에 있는 id를 받아올 수 있으므로

그 id를 기반으로 유저를 찾아서 user라는 변수에 할당한 다음 user를 템플릿 변수로 전달해준다.

 

이제 실행해보면 /users/무작위id 로 들어가면 home으로 돌아간다. 

일단은 이렇게 해놓고 나중에 수정할 것이다!

728x90
반응형