24~25강. 로그인 처리하기, 관리자 등록하기

한시현·2024년 4월 7일

UDR 백엔드 야생형

목록 보기
11/15

Section.3

24강. 로그인 처리하기

로그인 처리하기

관리자 계정을 만들어서 db에 추가하는 방법을 알아보자.
지금까지 만들었던 애플리케이션은 localhost에서 contacts 경로로 접근하면 db에 있는 전체 연락처를 가지고 와서 목록 형태로 보여주는 것이었다. contacts 경로 없이 그냥 / 경로로 접속하면 다음 화면이 표시된다.

위 화면을 수정해서 처음 / 로 접근했을 때 로그인 화면이 나타나도록 ejs 파일을 만들어 보겠다.

<%- include("./include/_header.ejs") %>

  <!-- Main -->
  <main id="site-main">

    <div class="home-container">
      <h3>로그인</h3>     
      <p>로그인이 필요한 서비스입니다.</p>

      <form class="login">
        <label for="username"><b>Username</b></label>
        <input type="text" placeholder="사용자 아이디" name="username" id="username">
        <label for="password"><b>Password</b></label>
        <input type="password" placeholder="비밀번호" name="password" id="password">
      
        <button type="submit">로그인</button>
      </form>
    </div>
  </main>
  <!-- /Main -->

  <%- include("./include/_footer.ejs") %>

이 home.ejs 코드를 활용해서 로그인 화면을 만들자.
이 화면은 /로 접속했을 때 나타나야 한다.

로그인과 관련된 라우트 코드를 따로 작성해 보도록 하자.
controllers 폴더 안에 loginController.js 파일을 만들자. 여기는 로그인과 관련된 함수들, 컨트롤러 함수들만 작성을 할 것이다. 우리는 /로 들어갔을 때 실행할 함수를 만들 것이다.

loginController.js

const asyncHandeler = require("express-async-handler");

// Get login page
// GET /
const getLogin = (req, res) => {
    res.render("home");
}

module.exports = getLogin;

이렇게 작성하면 / 경로로 접근했을 때 사용할 getLogin 함수가 완성된다. 모듈로 내보내주자.

컨트롤러 함수를 만들었으니 다음으로 라우팅을 해 줘야 한다.
routes 폴더에서 새 파일 loginRoutes.js를 만들고, 로그인과 관련된 라우트 코드만 작성 해 보겠다.

loginRoutes.js

const express = require("express");
const router = express.Router();
const getLogin = require("../controllers/loginController");

router.route("/").get(getLogin);

module.exports = router;

이렇게 필요한 모듈들을 가지고 온 다음 라우트에서 라우트 경로가 / 라면 GET 요청이 왔을 때 getLogin 함수를 실행해 주라고 작성하면 된다. 그 후 router를 내보내 주자.
여기서 getLogin 함수는 앞서 작성한 컨트롤러 안에 있고, 하는 일은 home.ejs 파일을 브라우저 창에 렌더링 하는 것이다.

이렇게 만든 라우트를 app.js에서 실행하도록 하자.

app.get("/", (req,res)=>{
    res.send("Hello Node");
});

이 부분을 지우고

app.use("/", require("./routes/loginRoutes"));

이렇게 새롭게 작성하면, loginRoutes를 이용해서 그 요청을 처리하겠다고 얘기해 주는 것이다.

한번 실행해보자. 서버가 켜져 있는지 확인은 필수다.

이렇게 로그인이 나타난다. 즉, / 경로로 GET 요청을 보내면 방금 만들었던 home.ejs 파일이 화면에 나타나게 된다.

로그인 테스트

지금은 contacts 경로로 접근 시 누구나 연락처를 볼 수 있게 되어있다.
그러나 우리가 로그인 창을 만든 이유는, 정해진 사람만 id와 pw를 입력해서 사용자가 확인이 되었을 경우에만 연락처를 볼 수 있게 하고 싶기 때문이다.

간단한 로그인 테스트를 해보자. id와 pw에 특정한 값을 넣었을 때 로그인이 되는지 보는 것이다.
로그인 화면에서 id와 pw를 입력한 후 [로그인]을 클릭했을 때 사용자가 입력한 정보를 서버로 보내야 하기 때문에 POST 요청 방식을 사용한다.

<form class="login" method="POST" action="/">

home.ejs의 from 태그에서 method를 POST로 지정할 것이다. 그리고 이 POST를 컨트롤러 함수에게 보내줘야 한다. /를 처리하는 컨트롤러 함수이므로, action도 위와 같이 지정해주면 된다.

이제 /경로로 POST 요청이 들어왔을 때 처리할 함수를 만들어 주면 되겠다.
loginController에 함수를 추가해보자.

const loginUser = asyncHandeler(async (req, res) => {
    const { username, password } = req.body;

    if (username === "admin" && password === "1234") {
        res.send("Login Success");
    }
    else {
        res.send("Login Failed")
    }
})

module.exports = {getLogin, loginUser};

loginUser라는 이름으로 POST 요청을 처리하는 함수를 작성할 것이다. 이는 서버에서 사용자 정보를 가져와 체크해야 하기 때문에 비동기로 처리를 해 줘야 한다. 요청 본문에서 사용자 이름(아이디), 비밀번호를 가지고 왔다. id와 pw가 위 코드와 같으면 로그인 성공, 아니면 실패했다고 표시해주겠다. 이렇게 만들었으니 이 친구도 내보내 주자.

이제 라우트 코드로 와서

const {getLogin, loginUser} = require("../controllers/loginController");

router.route("/").get(getLogin).post(loginUser);

이전의 코드 부분들을 이렇게 갱신해서 loginUser를 가지고 와주자. 이 loginUser는 POST 방식일 때 실행할 것이다.

이제 테스트를 해 보자.

사용자 이름에 admin, 비밀번호에 1234라고 넣은 다음 [로그인]을 눌러보자.

이렇게 성공적으로 출력된다. 우리가 만들었던 loginUser라는 컨트롤러 함수가 제대로 동작한다는 것을 알 수 있다.

만일 비밀번호를 다른 것을 입력한다면?

이렇게 로그인에 실패했다고 출력된다.

앞으로 이 loginUser라는 컨트롤러 함수는 조금 더 수정해서 db에 저장된 사용자 정보를 가져와서 비교하는 코드로 바꿀 것이다.

25강. 관리자 등록하기

관리자 등록하기

사용자 등록 화면을 만들고, 거기에 등록한 사용자 정보를 db에 저장하는 방법에 대해 알아보자. 관리자 뿐만 아니라 회원 등록을 위한 용도로도 사용할 수 있다.

register.ejs

<%- include("./include/_header.ejs") %>

  <!-- Main -->
  <main id="site-main">

    <h3>사용자 등록</h3>

    <form class="register">
      <label for="username"><b>아이디</b></label>
      <input type="text" placeholder="아이디" name="username" id="username">
      <label for="password"><b>비밀번호</b></label>
      <input type="password" placeholder="비밀번호" name="password" id="password">
      <label for="password2"><b>비밀번호 확인</b></label>
      <input type="password" placeholder="비밀번호 확인" name="password2" id="password2">
      <input type="submit" value="등록" class="register-btn">
    </form>    
  </main>
  <!-- /Main -->

  <%- include("./include/_footer.ejs") %>

해당 ejs 파일을 사용할 것이다. 이 파일을 언제 렌더링 할 것인지 컨트롤러 함수를 만들어주자.

loginController.js 中

const getRegister = (req,res) => {
    res.render("register");
}

이렇게 하면 register 경로로 GET 요청이 들어왔을 때 register.ejs 파일을 실행 할 컨트롤러 함수가 만들어졌다. 이 함수를 동작하게 하려면 라우트 코드를 작성해 줘야 한다.

module.exports = { getLogin, loginUser, getRegister };

모듈로 내보내주자.

loginRoutes.js

const express = require("express");
const router = express.Router();
const {getLogin, loginUser, getRegister} = require("../controllers/loginController"); // 코드 추가

router.route("/").get(getLogin).post(loginUser);
router.route("/register").get(getRegister); // 코드 추가

module.exports = router;

getRegister 함수도 가지고 오라고 추가 해 주겠다. register 경로로 GET 요청이 들어오면 해당 함수를 실행하라는 것이 추가된 라우터 코드이다.

이제 실행해보자.

register 경로로 요청했을 때 register.ejs 파일이 나타나는 것을 확인할 수 있다.

이제 사용자가 id와 비밀번호 확인을 넣고 [등록]을 클릭했을 때 db에 추가되도록 POST 요청을 처리해 줘야 한다. 그러면 db에도 사용자 정보가 들어갈 db가 필요하다.

우리가 몽고 db의 myContacts라는 db에는 contacts라는 연락처를 저장하는 컬렉션이 있다. 사용자 정보를 넣고 싶으면 사용자 정보를 넣을 컬렉션을 만들어 줘야 한다.

스키마를 만들고 스키마를 모델링해서 users라는 데이터베이스를 만들자.

userModel.js

const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const UserSchema = new Schema({
    username: {
        type: String,
        required: true,
        unique: true // 중복되면 안되기 때문에 unique 사용
    },
    password: {
        type: String,
        required: true
    }
});

module.exports = mongoose.model("User", UserSchema);

db이름을 User 라고 하게 되면 나중에 몽고 db에는 MyContacts 밑에 users 라는 복수 형태로 컬렉션이 만들어진다. 모델명은 단수로 지정하고 시작 글자를 대문자로 한다.

이제 위 코드를 저장하면 사용자 등록 화면에서 정보를 입력 후 POST 요청을 보내면 user라고 하는 db에 정보가 저장이 된다.

register.ejs

<form class="register" method="POST" action="/register">

register.ejs에서 register 경로로 POST 처리를 하라고 지정하자.

loginController.js

이제 비밀번호를 비교해주는 것이 남았다. 이는 컨트롤러에서 함수로 처리해줘야 한다.

const registerUser = asyncHandeler(async(req, res) => {
    const {username, password1, password2 } = req.body;
})

이 코드를 추가하자. 비밀번호가 같아야 db에 추가해줄 수 있다.

비밀번호 암호화하기 - bcrypt 모듈

웹에서 비밀번호를 사용할 때 이 비밀번호는 외부로 드러나선 안된다. 요즘에는 완전히 암호화 되어서 db에 저장되기 때문에 사이트 관리자조차 비밀번호를 알 수 없다. 이를 비밀번호 암호화라고 하는데, 그 중 많이 사용하는 방법이 bcrypt 모듈이다.

bcrypt 모듈사용자가 입력한 비밀번호를 다른 값으로 바꾼다. 이를 해시한다 라고 하는데 해시 함수를 제공한다.

1234라고 입력하면 이를 보고 다른 문자열로 바꾼다. 바뀐 문자열을 보고 다시 1234를 추측할 순 없다. 즉, 암호화만 가능하고 암호를 풀 수는 없다. 이는 일방향 함수이기 때문이다.

해시 함수는 위와 같은 특징들을 가지고 있기 때문에 안전하게 사용할 수 있다.

bcrypt 모듈을 설치 후에 bcrypt.hash라고 하는 함수를 사용하면 주어진 값을 해시시킬 수 있다.

해시를 반복할 횟수는 커질수록 암호화가 강력해지지만, 그만큼 시간이 많이 소요된다. 보통 10번 정도만 시킨다.
콜백 함수는 해시된 것을 사용해서 무엇을 할 것인지 지정해주는 것이다. 이 때 인자로는 err와 hash를 사용하는데 hash는 password를 해시한 결과 값이다.

우리는 이 해시 함수를 이용해서 로그인할 때 입력한 비밀번호와 사용자 등록 폼에 있는 비밀번호 1, 2도 해시 시킬것이다. 그 후 두 값이 같은지 비교할 것이다. 값이 같으면 해시 결과 값도 같다. 값을 비교할 때는 bcrypt.compare라는 함수를 사용한다.

간단한 설명을 보았으니 이제 bcrypt 모듈을 설치해 보자.

bcrypt 모듈 설치

npm i bcrypt

이제 여기 레지스터, 사용자를 등록하는 컨트롤러 함수를 작성 해 보겠다.
사용자를 db에 추가 할 것이다. loginController.js에 db와 아까 설치한 bcrypt를 가져오자.

const User = require("../models/userModel");
const bcrypt = require("bcrypt");

이제 registerUser로 가자.

const registerUser = asyncHandeler(async(req, res) => {
    const {username, password, password2} = req.body;
    if (password === password2) {
        const hashedPassword = await bcrypt.hash(password, 10);
        const user = await User.create({username, password:hashedPassword});
        res.json({message:"Register successful", user});
    }
    else {
        res.send("Register Failed");
    }
})

두 값이 같다면 사용자 정보를 db에 등록해도 된다. password1을 10번 해시(암호화)시킨 다음 hashedPassword라고 저장해준다.
그리고 create 함수를 이용해 db에 새로운 자료(사용자)를 추가한다.
또 제대로 들어갔는지 json 형식으로 메세지와 user 정보를 브라우저 화면에 보여달라고 할 것이다.
그렇게 만든 registerUser 모듈을 내보내주자.

module.exports = { getLogin, loginUser, getRegister, registerUser };

loginRoutes.js

const express = require("express");
const router = express.Router();
const {
    getLogin,
    loginUser,
    getRegister,
    registerUser // 코드 추가
} = require("../controllers/loginController");

router.route("/").get(getLogin).post(loginUser);
router.route("/register").get(getRegister).post(registerUser); // 코드 갱신

module.exports = router;

그리고 registerUser를 가져오고, post 요청이 왔을 때 registerUser라는 컨트롤러 함수를 실행하라고 지정해주면 된다.

이제 제대로 동작하는지 확인해보자.

이렇게 id를 admin, 비밀번호와 비밀번호 확인 역시 admin으로 입력하고 등록을 눌러보겠다.

이렇게 메세지와 등록된 사용자 정보가 출력되었다. password 또한 해시된 모습이 보인다.
이제 몽고 db를 확인해서 잘 저장되었는지 확인해보자.

이렇게 myContacts 안에 users 라는 새로운 db가 생겼고, 그 안에 우리가 등록한 사용자 정보가 들어가 있는 것을 확인할 수 있다.

0개의 댓글