반응형

이번 NodeJS 공부중 가장 중요한 부분.

User

user를 만드는 건 전에 작성했던 CRUD와 동일하다.

하지만 user를 만드는 데 있어 제일 중요한것 중에 하나가 개인의 개인정보를 보호하는 것이다. 그 중 password가 가장 민감한 부분일 것이다. 절대 db에 password를 있는 그대로 정장하면 안된다. password를 암호화해야한다.

Hashing

password를 암호화하는데 있어 Hashing이라는 기능을 사용하면 우리가 즐겨사용하는 비밀번호의 노출을 방지할수있다. 이를 사용하게 되면 예를 들어 Input 값이 1234라고 할때 Output은 'fqo9wiefn!@#Fqqiwoefj' 이런식의 변화가 이루어진다. 동일한 입력값은 동일한 출력값으로 나온다. 또한 기존 비밀번호의 순서를 1243 라고 바꿔준다면 'qwefjoqwenmfojqn!@#1230n' 전혀다른 비밀번호로 hashing이 된다. 하지만 반대로 output을 input값으로 넣는다면 기존 input값으로 나오지 않는다.

하지만 'Rainbow Table'라는 것이 있는데 이것을 가지고 역추적을해서 입력값을 얻어낼 수 있다. 하지만 이런것들 때문에 'Salt'라는 것이 생겼다. Salt는 랜덤 텍스트인데 비밀번호가 심플하더라도 Salt가 추가되어 password에 저장된다. 이는 Rainbow Table에도 없는 값이 된다. 이렇게 되면 비밀번호를 찾게 되는것도 어려워지는데 우리가 비밀번호 찾기 했을때 기존 비밀번호가 없어지고 다른 값을 받거나 바로 바꿀수있는 페이지로 이동하게 되는 경우가 바로 이런 경우로 인해 그렇다.

 

Bcrypt

https://www.npmjs.com/package/bcrypt

 

bcrypt

A bcrypt library for NodeJS.. Latest version: 5.1.1, last published: 4 months ago. Start using bcrypt in your project by running `npm i bcrypt`. There are 4269 other projects in the npm registry using bcrypt.

www.npmjs.com

npm i bcrypt

 

Status Code

회원가입 실패시 Status code를 함께 설정하여 브라우저가 실패를 인식하게 만들어줘야한다. 브라우저가 히스토리가 남지않게 이러한 조취들을 취해주어야한다.

	if (userNameExists) {
		return res.status(400).render("join", {
			pageTitle: "JOIN",
			idError: "아이디가 이미 사용중입니다.",
		});
	}

 

Input으로 들어온 비밀번호 Hashing 된 비밀번호 확인

await bcrypt.compare(password, user.password);

// 유저가 처음 입력한 비밀번호 , 해싱된 비밀번호

 

Sessions and Cookies

유저를 기억하게 만드는 방법. 브라우저가 너가 누군지 알게 만들어줘야한다. 

그 방법 중 한가지는 유저에게 Cookies를 보내주는 것이다. 

쿠키를 이해하기 앞서 Session부터 알아야한다.  Session은 브라우저와 백엔드 사이의 memory , history같은 것이다.

Session을 작동시키려면 백엔드와 브라우저가 서로에 대한 정보를 가지고 있어야한다. 따라서 로그인을 할때 자그마한 텍스트를 주게 되는데 이것을 가지고 유저에 대한 정보를 가질수 있게 된다. 

이 때 세션을 처리할 수 있도록 미들웨어를 만들어줘야하는데 express를 사용한다면 아래의 패키지가 도움을 줄것이다.

npm i express-session

 이것을 서버에 import 시키고 router들 가장 앞에 app.use를 이용해 사용해준다.

app.use(session({ secret: "hoho", resave: true, saveUninitialized: true }));

// resave
// 세션이 수정되지 않았더라도 요청이 왔을 때 세션을 다시 저장할 지 여부를 결정합니다.

// saveUninitialized
// 세션이 새로 만들어지고 수정된 적이 없을 때 uninitialized
// 쉽게 말해 새로운 세션이 있는데 수정된 적이 없으면 최화되지 않는 것이다.
// 세션을 수정할 때만 세션을 DB에 저장하고 쿠키를 넘겨주는것

//gpt
// 세션이 초기화되지 않은 상태에서 요청이 왔을 때 세션을 저장할지 여부를 결정합니다.
// true로 설정하면 초기화되지 않은 세션이 있는 요청에 대해 새로운 세션을 저장하게 됩니다. 
// 이것은 사용자가 세션을 사용하기 전에 세션을 초기화하는 데 도움이 됩니다.

secret은 아무도 모르는 string으로 작성하게 될것이다. 

app.get("/see-session", (req, res) => {
	return res.send(req.session.id);
});

express는 알아서 그 브라우저를 위한 세션 id를 만들것이다.

 

이제 브라우저에게 어떤 사람이 로그인 했는지 세션에 넣어줘야한다. 세션저장소에 세션을 보면

app.use((req, res, next) => {
	if (req.sessionStore && typeof req.sessionStore.all === "function") {
		req.sessionStore.all((err, sessions) => {
			console.log(sessions);
			next();
		});
	}
});

오브젝트를 받을 수 있는데 세션아이디와 쿠키가 적혀져있는 오브젝트를 받을 수 있다. 여기에 유저의 정보를 넣어주면 된다. 넣는 방법은 간단한데. 로그인 시에 넣어주면 된다. 작성한 로그인 컨트롤러에 가서  아래와 같이 작성한다.

const session = req.session;
	session.loggedIn = true;
	session.user = user;

타입스크립를 사용해서 작성하게 되면 express-session에는 loggedIn과 user에 대한 타입은 명시되지 않아 오류가 날수 있다. 따라서 이를 해결하기 위해 두가지 방법이 있는데 express-session 파일에 SessionData 타입에가서 직접 타입을 명시해주는 방법이 있고 declare module을 사용해서 명시해주는 방법이 있다.

 

이를 이용해 FrontEnd에서 유저가 로그인하면 Navgation Bar에 있는 login 과 logout에 대한 내용을 보여지게 혹은 숨기게 할 수 있는데  pug에서는 req.session에 직접 전달하는 것이 아니면 접근하는게 어렵다. 이런 경우에는 res.locals를 사용해서 접근할 수 있다. 이것을 전역으로 사용가능한 미들웨어로 만들어준다.

// server.ts

app.use(middlewarename)

// middleware.ts

export const pugLocalMiddleware: ExpressRouter = (req, res, next) => {
	res.locals.loggedIn = req.session.loggedIn ?? false;
	res.locals.loggedUser = req.session.user ?? null;
	console.log(res.locals);
	next();
};

 

Session Data

express-session을 사용하면 Session Id는 쿠키안에 저장이 되는데 Session Data는 쿠키안에 저장이 안되어 있다. session data는 서버쪽에 저장이 된다. 서버에 저장되는 default session storage는 휘발성이고 실제로 사용하기 위한 것은 아니다. 그래서 이것을 실질적으로 사용하려면 DB에 Session을 저장할 수 있게 session store를 사용해야한다. 이를 이용하면 서버가 재부팅을 하더라도 저장된 session data가 있으면 지속적인 로그인을 할 수 있다.

 

하지만 session을 DB에 모두 저장을 하는 것은 좋은 생각이 아니다. 익명의 유저에게는 주지 않고 로그인한 유저들에게 만주는 것이 바람직하다. 

 

이걸 위한 해결책이 있는데 바로 token authentication이다. 

예를 들어 핸드폰 앱을 만들 때 이것들은 쿠키를 갖지 않게 때문에 token을 사용해야한다. 하지만 브라우저이기 때문에 token을 사용해도 되고 안해도 된다.

 

Cookie Property

Secret은 우리가 쿠키에 sign할 때 사용하는 string. 쿠키에 sign하는 이유는 backend가 쿠키를 줬다는 걸 보여주기 위함. 왜냐면 session hijack이라는 공격 유형이 있기 때문이다. 이건 길고 강력하며 무작위로 작성되어야 한다.

 

Domain은 쿠키를 만든 backend가 누구인지 알려준다. 브라우저는 Domain에 따라 쿠키를 저장하도록 되어 있다. 그리고 쿠키는 Domain에 있는 backend로만 전송이 된다.

 

Expires은 쿠키의 만료 날짜이다. 만료날짜를 지정하지 않으면 session cookie로 설정이되고 사용자가 닫으면 session cookie는 끝나게 된다. Max-Age는 언제 세션이 만료 되는지 알려준다.

app.use(
	session({
		secret: "",
		resave: false,
		saveUninitialized: false,
		store: MongoStore.create({
			mongoUrl: "",
		}),
        cookie:{
            maxAge: 밀리세컨드
        }
	}),
);

 

Environment Variables(환경변수)

Root 디렉토리에  '.env' 파일을 만들고 환경변수에 대한 선언을 해준다. 변수의 key 값은 대문자로 이루어져야한다.

KEY_NAME=어쩌구저쩌구

이것을 사용하기 위해서는 process.env.KEY_NAME 과 같이 사용해야한다.

만약 오류가 났다면 dotenv를 설치를 해야한다. 또한 서버가 시작되는 제일 처음 시작점에 import나 require문을 사용하여 config를 불러와야한다. 타입스크립트를 사용하게 되면 타입을 설정해줘야한다. as 키워드나 non - null assertion 연산자인 (!) 느낌표를 활용하는 것이 보기에 좋아보인다.

728x90

+ Recent posts