본문 바로가기
포스코x코딩온

[포스코x코딩온] 풀스택 부트캠프 7주차 정리 2 - Cookie, Session, dotenv, aws, 서버구축, jwt

by 김선지 2023. 12. 9.

Cookie

웹 브라우저에 저장되는 key value 쌍의 데이터파일. 

(key, value, 만료일, 경로정보)로 구성됨

 

쿠키의 동작 방식

 

1. 클라이언트가 페이지 요청

2. 서버에서 쿠키 생성해서 HTTP 헤더에 쿠키 포함시켜 응답 (쿠키 만료기간이 있으면 브라우저 닫혀도 사라지지 않음)

3. 클라이언트가 같은 요청을 할 경우 HTTP헤더에 쿠키를 같이 보냄 (요청 + 나 네가 쿠키 준 그 사용자다 어필.)

4. 서버에서 쿠키를 읽어 이전 상태 정보를 변경 할 필요가 있을 때 쿠키를 업데이트 해서 변경된 쿠키를 HTTP헤더에 포함시켜 응답

 

그냥 서버에서 클라이언트에게 뿌려주는 데이터 조각이라고 생각하면 될 것 같다.

 

app.js

const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
const PORT = 8000;

app.set('view engine', 'ejs');
app.set('views', './views');

// cookie-parser

// 일반 쿠키
// app.use(cookieParser());

// 암호화 쿠키
app.use(cookieParser('mySecretKey'));
// secretKey: 비밀키
// - 서명된 쿠키가 있는 경우, 제공한 비밀키를 가지고 해당 쿠키가 내 서버가 만든 쿠키임을 인증 가능
// - 쿠키는 클라이언트에서 위조가 쉬우므로 비밀키를 통해 만든 서명을 쿠키 값 뒤에 붙임
// - 서명된 쿠키는 req.cookies -> req.signedCookies 객체에 들어있음

// cookie 옵션 객체
const cookieConfig = {
    // httpOnly: 웹 서버를 통해서만 쿠키에 접근 가능
    // maxAge: 쿠키의 수명, 단위는 밀리초, 24시간: 24 * 60 * 60 * 1000
    // expires: 만료 날짜를 GMT 시간대로 설정
    // domain: 쿠키가 전송될 도메인을 특정 가능 (default: 현재 도메인)
    // secure: 웹브라우저와 웹 서버가 https로 통신하는 경우에만 쿠키를 서버에 전송
    // signed: 쿠키의 암호화 결정 (req.signedCookies 객체에 들어있음)
    httpOnly: true,
    maxAge: 60 * 1000, // 1분
    signed: true, // 암호화 쿠키
}

app.get('/', (req, res) => {
    res.render('cookie');
})

// 쿠키 설정
app.get('/setCookie', (req, res) => {
    // res.cookie(쿠키 이름, 쿠키 값, 쿠키 옵션)
    // 쿠키를 생성해서 브라우저에 쏴줌.
    res.cookie('myCookie', 'myValue', cookieConfig);
    res.send('set cookie!');
})

// 쿠키 확인
app.get('/getCookie', (req, res) => {
    // res.send(req.cookies); // 일반 쿠키
    res.send(req.signedCookies); // 암호화 쿠키, 객체로 반환
    // res.send(req.signedCookies.myCookie; // key 값이 myCookie인 하나만 해서 반환 (value값)
})

// 쿠키 삭제
app.get('/clearCookie', (req, res) => {
    // res.clearCookie(키, 값, 옵션)
    res.clearCookie('myCookie', 'myValue', cookieConfig);
    res.send('clear cookie!')
})

app.listen(PORT, () => {
    console.log(`http://localhost:${PORT}`);
})

Session

 

웹 서버에 저장되는 쿠키

사용자가 웹 브라우저를 통해 접속한 시점부터 연결 끝내는 시점까지 일련의 요구를 하나의 상태로 보고 그 상태를 유지시킴. 즉 세션도 쿠키다. 다만 서버에 저장될 뿐

쿠키보다 보안성은 높지만 쿠키를 두개만드는 거라 속도는 느리다.

 

1. 클라이언트가 서버에 요청 시 세션 ID 발급받아서 쿠키에 저장  (세션에 대한 식별키)

2. 이후 클라이언트가 서버에 요청할 때 쿠키의 세션 ID 서버에 선달

3. 세션 ID를 전달받은 서버는 세션 ID에 상응하는 클라언트 정보를 가져옴.

4. 클라이언트 정보를 가지고 서버는 클라이언트에게 응답

 

EX) 로그인 유지

 

세션을 이용할 때 req.session.id라는 객체는 이미 디폴트값으로 할당이 되어있으니 id를 할당할땐 다른 key값을 이용하자.

정의되지 않은 세션을 호출할 때는 undefined가 뜬다. 에러가 안떠서 다행이다.

 

app.js

const express = require('express');
const session = require('express-session');
const app = express();
const PORT = 8000;

app.set('view engine', 'ejs');
app.set('views', './views');

// 세션 옵션 객체
// secure: 값을 true로 하면 https에서만 세션을 주고 받음
// secret: 안전하게 쿠키를 전송하기 위한 쿠키 서명 값 (세션을 발급할 때 사용되는 키)
// resave: 세션에 수정사항이 생기지 않더라도, 매 요청(req)마다 세션을 다시 저장할 것인지, 세션을 항상 저장할 건지 지정하는 값 (false 권장)
// saveUninitialized: 세션에 저장할 내역이 없더라도 처음부터 세션을 생성할 지 설정
// httpOnly: 웹 서버를 통해서만 쿠키에 접근 가능
// maxAge: 쿠키 수명 (단위 ms)
// => cookie 객체에 넣어서 정의

// 세션 옵션 정의
app.use(session({
    secret: 'mySessionSecret',
    resave: false,
    saveUninitialized: true,
    cookie: {
        httpOnly: true,
        maxAge: 60 * 1000
    }
}))

app.get('/', (req, res) => {
    res.render('session');
})

app.get('/set', (req, res) => {
    // 세션 설정 req.session.키 = 값
    req.session.name = "홍길동"; // { name: "홍길동" }
    res.send('세션 설정 완료!');
})

app.get('/name', (req, res) => {
    console.log(req.session.name); // 홍길동
    console.log(req.sessionID); // 현재 세션 아이디
    console.log(req.session);
    res.send({ id: req.sessionID, name: req.session.name });
})

app.get('/destroy', (req, res) => {
    req.session.destroy((err) => {
        if (err) {
            console.log(err);
            res.send('fail');
        }
        res.redirect('/name'); // 세션 객체 전체가 사라짐
    })
})

app.listen(PORT, () => {
    console.log(`http://localhost:${PORT}`);
})

 


 

환경변수

OS나 애플리케이션에서 사용되는 데이터 값을 저장하는 메커니즘.

Node.js 에서는 process.env 객체를 통해서 환경변수에 접근한다.

로컬이 아닌 곳에서 작업할 때는 민감한 정보를 올리면 안되기 때문에 이렇게 따로 env로 환경변수를 만들어 저장한다.

쉽게 말하면 보여주면 안되는 정보 (비밀번호, 포트번호) 같은 것을 가리는 용도로 쓰인다.

dotnev를 이용했다.

 

app.js

const express = require('express');
const app = express();
const dotenv = require('dotenv');
dotenv.config();

const PORT = process.env.PORT;

app.get('/', function(req, res) {
    console.log(process.env.NAME); // what the
    console.log(process.env.NODE_ENV); // dev
    console.log(process.env.KKK); // kimtakgu
    res.send('HELLO WORLD!');
})

app.listen(PORT); // 8000

 

.env

NAME=what the
NODE_ENV=dev
KKK=kimtakgu
PORT=8000

 


AWS

아마존에서 제공하는 클라우드 서비스다. 클라우드를 구축할 수 있다.

 

EC2 (Elastic Compute Cloud) : 독립된 컴퓨터를 임대해주는 클라우드 서비스.얘를 서버로 쓰면 된다.

쉽게 말하면 클라우드를 이용해서 컴퓨터 한대를 이용할 수 있다.

(putty로 EC2에 연결해서 cmd 창을 이용할 수 있고, file zilla를 이용해서 클라우드에 파일을 넣을 수 있는 UI를 제공받을 수 있다.)

이거 좀 엄준식같다

 

S3 (Simple Storage Servise) : AWS에서 제공하는 스토리지 서비스, 쉽게 말하자면 데이터(파일)를 저장할 수 있는 공간을 제공해주는 서비스이다.

Bucket: S3에서 생성되는 최상위 디렉토리와 객체를 저장

Object: S3에서 저장되는 데이터로 파일과 메타데이터로 이루어져 있음

Key: 객체를 찾기 위해 사용되는 고유 식별자로 '디렉토리' + '파일명'으로 이루어짐

Region: 내가 사는 지역

 


웹 서버 

정적 파일(static file)을 전달하는 서버, css나 views와 같은 파일을 전달하는 서버

Apache나 Nginx 같은프로그램이 있다.

이걸 활용해서 서버를 시작하면 로컬에서 npm app.js 하던 것처럼 시작할 수 있다. = 서버 구축, 배포

putty cmd 창에서 서버와 연결해주고

sudo apt-get install -y nodejs << 로 nodejs 설치해주고

sudo npm i -g pm2 << 로 node.js 어플리케이션을 쉽게 관리해주는 프로세스 매니저인 pm2를 다운로드 하고

 

cd로 폴더 경로 들어가서

pm2 start index.js << 로 실행하면 된다

 


JWT (JSON WEB TOKEN)

oAuth : 사용자의 계정에 접근할 수 있는 권한을 부여하기 위한 프로토콜이다.

그리고 권한을 식별하는데 사용되는 토큰이 JWT (로그인 되었음을 증명하는 토큰)이다.

쉽게 말하자면 사용자가 계정에 접근할 수 있는 권한을 가졌음을 증명하는 토큰이다. (아이디 비밀번호를 직접 저장하면 다 털린다.)

즉, 로그인하면 발급되게 설정해준다.

 

app.js

const express = require('express');
const jwt = require('jsonwebtoken');
const PORT = 8000;

const SECRET = '651s5v1w65gesv1w356h156ne48ev645w16gw54v';
// 암호화 하는데 필요한 비밀키로 환경변수로 설정해주자. 지금은 아무렇게나 적었다.

const app = express();

const userInfo = { id: 'banana', pw:'1234', name: '홍길동', age: 26};
// db에서 가져왔다고 치는 user 정보

app.set('view engine', 'ejs');
app.set('views', './views');

app.use(express.urlencoded({ extended: true}));
app.use(express.json());

app.get('/', function(req, res) {
    res.render('index');
});

app.post('/login', function(req, res) {
    try {
        const { id, pw } = req.body;
        const { id: realId, pw: realPw } = userInfo;

        if(id === realId && pw === realPw) {
            // 토큰 생성
            // jwt.sign(payload, secret Or PrivateKey, [optaions, callback])
            const token = jwt.sign({id: id}, SECRET);
            res.send({isLogin: true, token}); // 토큰을 프론트에 보내준다. 세션에 저장해도 될듯.
        } else {
            res.send({isLogin: false, msg: '로그인 정보가 올바르지 않습니다!'});
        }
    }catch (err) {
        console.error(err);
    }
})

app.post('/token', function(req, res) {

    console.log('token > ', req.headers.authorization);

    if(req.headers.authorization) {
        const authorization = req.headers.authorization.split(' ');
        console.log(authorization); // ['Bearer, 'token_string];
        const token = authorization[1];
        
        try {

            // 토큰 검증 : jwt.verify(token, secretkey)
            const result = jwt.verify(token, SECRET);
            console.log('result >', result)
    
            if(result.id === userInfo.id) {
                res.send({isVerify: true, name: userInfo.name});
            } else {
                res.send({isVerify: false, msg: '잘못된 접근입니다!'});
            }
        } catch (err) {
            console.log('verify err > ', err);
            res.send({ isVerify: false, msg: '인증된 회원이 아닙니다!'});
        }
    } else {
        res.redirect('/login');
    }
});

app.get('/login', function(req, res) {
    res.render('login');
});


app.listen(PORT, function() {
    console.log(`I am listening at ${PORT}`);
});