본문 바로가기
Express, Next.js/Express

[express] express-session 로그인, 로그아웃 구현하기

by 방배킹 2024. 4. 16.


express-session

express에서 session을 사용하려면 express-session 모듈이 필요하다

 

https://www.npmjs.com/package/express-session

 

express-session

Simple session middleware for Express. Latest version: 1.18.0, last published: 3 months ago. Start using express-session in your project by running `npm i express-session`. There are 4896 other projects in the npm registry using express-session.

www.npmjs.com

 

express-session을 다운 받자

npm install express-session

npm install @types/express-session # typescript 사용시

 

 

 

 

spring에서는 tomcat 내장 서버에 session 정보를 저장했었다.

 

express는 어디에 저장을 할까?

 

 

기본 저장소는 MemoryStore 이다. 프로덕션으로 사용하기에는 문제가 있고, 디버깅 및 개발용이라고 한다.

 

학습을 위해 사용할 예정이므로 기본 저장소를 사용하겠다.

 

다른 저장소들은 아래 링크에서 확인 가능하다.

https://www.npmjs.com/package/express-session#compatible-session-stores

 

express-session

Simple session middleware for Express. Latest version: 1.18.0, last published: 3 months ago. Start using express-session in your project by running `npm i express-session`. There are 4896 other projects in the npm registry using express-session.

www.npmjs.com

 

var app = express()
app.set('trust proxy', 1) // trust first proxy
app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: true }
}))

 

공식 문서에는 위와 같은 방법으로 session을 사용한다고 나와있었다.

 

secure: true는 추천 옵션이며, 보안 관련 설정이다. 간단하게 아래와 같이 사용하였다.

app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: false }
}))

 

세션 로그인 구현 코드

// 세션 로그인
router.post('/session/login',async(req:Request,res:Response)=>{
    try{
        const {userEmail,password} = req.body;
        const userdata:User|null = await UserModel.findByUserEmail(userEmail);

        //존재하지 않는 사용자
        if (!userdata) {
            return res.status(400).json({ message: '이메일을 다시 확인해주세요.' });
        }

        const passwordMatch = await bcrypt.compare(password,userdata.password);
        if(passwordMatch){
            req.session.user = {
                userEmail: userEmail,
                nickname: userdata.nickname,
                loginTime: new Date().toISOString(),
                authorized: true,
            };
            console.log(userEmail,' 로그인');
            console.log(req.session.user.userEmail);
            res.status(200).json({ session:req.session.user });
        }else{
            res.status(400).json({ message: '비밀번호를 다시 확인해주세요.' });
        }
    }catch(error){
        console.error('session login error :', error);
        res.status(500).json({ message: '로그인 오류' });
    }
});

 

userEmail과 password를 받아서 데이터베이스에서 email을 이용해 user 정보를 가저온뒤 password를 비교한다.

비밀번호가 일치하면 session에 데이터를 저장한다,

 

user에 계속 빨간줄이 생기고 오류가 발생하여 오류 메시지를 확인하고 Sessiondate에 user를 정의 하여 해결했다.

declare module 'express-session' {
  interface SessionData {
    user?: {
      userEmail: string;
      nickname: string;
      loginTime: string;
      authorized: boolean;
    };
  }
}

 

 

 

잘 작동한다.

// 세션 로그아웃
router.get('/session/logout',(req:Request,res:Response)=>{
    try{
        req.session.destroy(()=>{
            const userEmail = req.session?.user?.userEmail;
            console.log(userEmail,' 로그아웃');
            res.status(200).json({ message:`${userEmail} 로그아웃` });
        });
    }catch(error){
        console.error('session logout error:', error);
        res.status(500).json({ message: '로그아웃 오류' });
    }
})

 

로그아웃을 할때 user email이 undefined로 나오는 오류(?)가 발생했다.

 

 

로그인도 잘되고 session에도 잘 들어가는데 왜 로그아웃이 안되지 한참 고민했는데 destroy() 를 호출하면 session이 사라지기 때문에 당연히 undefined이 출력된다....

 

const userEmail = req.session?.user?.userEmail;
req.session.destroy(()=>{
    console.log(userEmail,' 로그아웃');
    res.status(200).json({ message:`${userEmail} 로그아웃` });
});

 

req.session.destroy()를 호출하기전에 값을 저장한뒤 destroy를 할때 출력해주면된다.

 

최종 코드

// 세션 로그인
router.post('/session/login',async(req:Request,res:Response)=>{
    try{
        const {userEmail,password} = req.body;
        const userdata:User|null = await UserModel.findByUserEmail(userEmail);

        //존재하지 않는 사용자
        if (!userdata) {
            return res.status(400).json({ message: '이메일을 다시 확인해주세요.' });
        }

        const passwordMatch = await bcrypt.compare(password,userdata.password);
        if(passwordMatch){
            req.session.user = {
                userEmail: userEmail,
                nickname: userdata.nickname,
                loginTime: new Date().toISOString(),
                authorized: true,
            };
            console.log(userEmail,' 로그인');
            res.status(200).json({ session:req.session.user });
        }else{
            res.status(400).json({ message: '비밀번호를 다시 확인해주세요.' });
        }
    }catch(error){
        console.error('session login error :', error);
        res.status(500).json({ message: '로그인 오류' });
    }
});
// 세션 로그아웃
router.get('/session/logout',async(req:Request,res:Response)=>{
    try{
        const userEmail = req.session?.user?.userEmail;
        await req.session.destroy(()=>{
            console.log(userEmail,' 로그아웃');
            res.status(200).json({ message:`${userEmail} 로그아웃` });
        });
    }catch(error){
        console.error('session logout error:', error);
        res.status(500).json({ message: '로그아웃 오류' });
    }
});

 

다음에는 토큰을 이용한 로그인, 로그아웃을 구현해보자.

댓글