로그인 기능 구현

2025. 11. 27. 21:35·DB

로그인 기능의 핵심: 데이터베이스

 

로그인 기능 동작 원리

 

  1.  사용자가 아이디와 비밀번호를 입력하고 `로그인` 버튼을 클릭.
  2.  서버는 사용자가 입력한 아이디를 데이터베이스에서 검색.
  3.  해당 아이디가 존재하면, 저장된 비밀번호와 사용자가 입력한 비밀번호가 일치하는지 비교.
  4.  모두 일치하면 `로그인 성공`, 그렇지 않으면 `로그인 실패`를 응답.

 

로그인 기능에는 사용자 정보를 저장하고 조회할 수 있는 데이터베이스가 반드시 필요함.

 

1. 사용자 테이블 구조 설계

사용자 정보를 저장할 `users` 테이블을 설계. 로그인 기능에 필수적인 정보는 아이디와 비밀번호. 사용자를 구분할 고유 번호 (id)를 추가 -> 테이블 구성.

 

`users` 테이블 구조

칼럼 이름 자료형 제약 조건 설명
id INTEGER PRIMARY KEY AUTOINCREMENT 사용자 고유 번호 (자동 증가)
username TEXT NOU NULL, UNIQUE 사용자 아이디 (필수 입력, 중복 불가)
password TEXT NOT NULL 사용자 비밀번호 (필수 입력)

 

  • UNIQUE : `username` 칼럼에 추가된 제약 조건. 이메일이나 아이디처럼, 다른 사용자와 절대 중복되어서는 안 되는 값에 설정.

실제로 비밀번호를 저장할 때는 `해싱(Hashing)`이라는 암호화 과정을 거쳐, 원래 값을 알아볼 수 없는 형태로 변환하여 저장해야 함. (예: `bcrypt` 패키지 사용)

 

2. 프론트엔드와 백엔드 코드 분리 설계

  •  public 폴더 : 브라우저가 직접 접근하여 화면에 그리는 파일들(HTML, 프론트엔드, JS, CSS 등)을 담음.
  •  server.js : 데잍터베이스를 제어하고, 프론트엔드의 요청에 응답하는 API 서버의 역할을 함.

3. 로그인 상태 유지 설계 (feat. JWT)

HTTP 통신은 기본적으로 `상태가 없는 (Stateless)` 특성을 가짐. 즉, 서버는 방금 전의 요청을 기억하지 못함. 그래서 로그인 한 후 다른 페이지로 이동하면, 서버는 우리가 로그인했다는 사실을 잊어버림. 이것을 방지하기 위해 JWT 사용.

 

자유이용권: JWT (Json Web Token)

서버는 사용자의 고유 정보 (예: id)를 담아 암호화된 토큰(Token)을 생성하여 사용자에게 전달. 이 토큰은 비밀키로 서명되어 있어 위조가 불가능.

 

로그인 상태 유지 흐름

  1.  로그인 요청 : 사용자가 `login.html` 에서 아이디/비밀번호를 입력, 프론트엔드 JS는 이 정보를 서버의 `api/login` 으로 보냄.
  2.  서버의 인증 및 토큰 발급 : 서버는 DB를 확인하여 정보가 일치하면, 해당 사용자의 정보를 담은 JWT(자유이용권)를 생성 프론트엔드에 응답으로 보내줌.
  3.  토큰 저장 : 프론트엔드 JS는 서버로부터 받은 JWT를 브라우저의 안전한 저장 공간 ( localStorage )에 저장.
  4.  인증이 필요한 요청 : 사용자가 로그인 후에만 접근 가능한 페이지 ( 예: `main.html` 의 사용자 정보 ) 를 요청할 때, 프론트엔드 JS는 저장해 둔 JWT를 요청 헤더 (Header)에 포함하여 서버로 보냄.
  5.  서버의 인가 : 서버는 요청에 포함된 JWT가 유효한지 (위조되지 않았는지) 검증하고, 유효한 경우에만 요청을 허락해 데이터를 보내줌. 유효하지 않으면 "로그인이 필요합니다" 와 같은 에러를 응답함. 

4. 페이지 서빙

node.js 서버가 사용자가 접속했을 때 눈에 보이는 화면, 즉 프론트엔드 파일들(login.html, main.html 등)을 보내주는 것.

 

http 모듈로 서버를 만들고, fs 모듈로 요청된 URL에 해당하는 파일을 읽어 브라우저에 전송.

 

5. 로그인 APi (`POST/api/login`)

로그인 API는 프론트 엔드로부터 사용자가 입력한 아이디와 비밀번호를 받아, 데이터베이스에 저장된 정보와 일치하는지 검증하는 핵심적인 역할을 함.

 

로그인 처리 흐름

  1.  프론트엔드가 fetch 를 이용해 /api/login 경로로 POST 요청을 보냄. 이때 요청 본문(body)에  { username: `...`, password: `...` } 형태의 JSON 데이터를 전달.
  2.  서버는 요청 본문(body)의 JSON 데이터를 파싱하여 아이디와 비밀번호를 추출.
  3.  SELECT SQL 문을 사용, 데이터베이스의 users 테이블에서 프론트엔드가 보낸 아이디(username)와 일치하는 사용자를 찾음.
  4.  [분기 처리]
  • 사용자를 찾지 못한 경우 : "존재하지 않는 아이디입니다." 와 같은 실패 메시지를 응답.
  • 사용자를 찾은 경우 : 데이터베이스에 저장된 비밀번호와 프론트엔드가 보낸 비밀번호가 일치하는지 비교.

  5.  [비밀번호 비교 결과]

  • 비밀번호가 일치하지 않는 경우 : "비밀번호가 틀렸습니다." 와 같은 실패 메시지를 응답.
  • 비밀번호가 일치하는 경우 : 로그인 성공! 다음 단계인 JWT를 발급하여 성공 응답과 함께 프론트엔드로 보내줌.

JWT 처리

JWT 패키지 설치 : npm install jsonwebtoken

 

JWT 생성 (토큰 발급)

jwt.sign() 함수를 사용하여 토큰을 생성.

 

JWT 검증 (자유이용권 확인)

로그인 후, `마이페이지`처럼 인증된 사용자만 접근할 수 있는 API를 요청할 때, 클라이언트는 발급받은 JWT를 요청 헤더(Header)에 담아 보냄. 서버는 이 토큰이 유효한지 검증해야 함.

 

6. JS 코드 (`public/app.js`)

하나의 app.js 파일이 현재 어떤 HTML 페이지에 있는지 스스로 판단, 각 페이지에 맞는 기능을 수행하도록 코드를 작성.

 

브라우저 저장소: `localStorage`

서버로부터 받은 JWT 토큰은 브라우저를 껐다 켜도 유지되어야 함.

localStorage는 이러한 데이터를 브라우저에 영구적으로 저장할 수 있게 해주는 간단한 저장 공간.

 

  • 저장 : localStorage.setItem('token', 'JWT_TOKEN_HERE')
  • 읽기 : localStorage.getItem('token')
  • 삭제 : localStorage.removeItem('token')

 

프로젝트 파일 구조 

login-app/
├── public/
│   ├── login.html
│   ├── signup.html
│   ├── main.html
│   └── app.js
├── users.db
└── server.js

 

Full-Stack 핵심 개념 가이드

서버와 클라이언트가 소통하는 방식 (HTTP 매서드, CORS) 과 프론트엔드 코드 구성의 원리.

 

1. API 요청/처리시 사용되는 method

우리가 `fetch` 를 사용해 서버에 요청을 보낼 때, method: 'POST' 와 같이 '메서드(method)'를 지정. HTTP 요청 메서드는 클라이언트 (브라우저)가 서버에게 요청의 목적이나 의도가 무엇인지 알려주는 '행동 동사'와 같음.

 

데이터베이스의 CRUD (Create, Read, Update, Delete) 작업은 각각의 HTTP 메서드와 관례적으로 연결됨.

 

HTTP 메서드 CRUD 역할 설명
GET Read (읽기) 서버로부터 데이터를 조회할 때 사용. (예: 게시물 목록 보기)
POST Create (생성) 서버에 새로운 데이터를 생성할 때 사용. (예: 회원가입, 새 글 작성)
PUT Update (수정) 기존 데이터를 전체적으로 수정할 때 사용. (예: 회원 정보 전체 수정)
DELETE Delete (삭제) 기존 데이터를 삭제할 때 사용. (예: 게시물 삭제)

 

2. CORS

CORS (Cross-Origin Resource Sharing, 교차 출처 리소스 공유)는 웹 브라우저에서 실행되는 스크립트가 다른 출처 (Origin)의 리소스에 접근할 수 있도록 허용하는 보안 메커니즘 

 

이것을 이해하려면 `동일 출처 정책(Same-Origin Policy)` 을 알아야 함. 이 정책은 "A라는 웹사이트에서 실행된 스크립트는 A 웹사이트의 리소스만 접근할 수 있다"는 기본적인 웹 보안 규칙. (예: "우리 집 안에서는 우리 집 물건만 쓸 수 있다")

 

'출처(Origin)' : URL의 프로토콜(http), 호스트(localhost), 포트(:3000) 세 가지를 조합한 것.

 

CORS는 다른 출처의 서버가 "내 데이터는 저 웹사이트에서 사용해도 좋아" 라고 허락해 주는 '허가증'과 같은 역할을 함.

 

3. CORS Preflight 요청과 헤더 설정

CORS Preflight 요청 (`OPTIONS` 메서드)

우리가 만든 프론트엔드 코드가 Content-Type: 'application/json' 과 같은 특별한 헤더를 담아 서버에 `POST` 요청을 보내려고 할 때, 브라우저는 본 요청을 보내기 전에 예비 요청(Preflight Request)을 보냄.

 

이 예비 요청은 OPTIONS 라는 HTTP 메서드를 사용, 서버가 이 요청에 대해 POST 메서드 허용, Content-Type 헤더도 허용 하는 응답을 보내주면 그제서야 원래 보내려던 `POST` 요청을 보냄.

 

CORS 헤더 설정

Access-Control-Allow-Origin : 어떤 출처의 요청을 허용할지 지정. (예 : ' * ' 전부 허용)

Access-Control-Allow-Methods : 허용할 HTTP 메서드 목록을 지정. (예 : 'POST, GET, PUT, DELETE, OPTIONS')

Access-Control-Allow-Headers : 허용할 요청 헤더 목록을 지정. (예 : 'Content-Type, Authorization')

 

4. `app.js` 파일 하나로 여러 페이지를 제어하는 원리

"현재 페이지에 해당 요소가 존재하는지 먼저 확인하기"

 

JavaScript에서 document.querySelector("#존재하지_않는_id")를 실행하면, 에러가 발생하는 대신 null 값이 반환됩니다. if문에서 null은 false로 취급.

 

이 원리를 이용해 다음과 같이 코드를 구성할 수 있음.

document.addEventListener('DOMContentLoaded', () => {
    // 1. 각 페이지에 있을 법한 요소들을 일단 모두 선택해 본다.
    const loginForm = document.querySelector("#login-form");
    const signupForm = document.querySelector("#signup-form");
    const welcomeMessage = document.querySelector("#welcome-message");

    // 2. 각 요소의 존재 여부를 if문으로 확인하여 로직을 분기한다.

    // loginForm 변수에 요소가 담겨있다면 (null이 아니라면),
    // 현재 페이지는 login.html이라고 판단할 수 있다.
    if (loginForm) {
        // 이 안의 코드는 login.html에서만 실행됩니다.
        console.log("여기는 로그인 페이지입니다.");
        loginForm.addEventListener('submit', (e) => {
            // ... 로그인 관련 로직 ...
        });
    }

    // signupForm 변수에 요소가 담겨있다면,
    // 현재 페이지는 signup.html이라고 판단할 수 있다.
    if (signupForm) {
        // 이 안의 코드는 signup.html에서만 실행됩니다.
        console.log("여기는 회원가입 페이지입니다.");
        // ... 회원가입 관련 로직 ...
    }
    
    // ... main.html에 대한 로직도 마찬가지 ...
});

 

이러한 '특징 감지(Feature Detection)' 방식을 통해, 우리는 각 페이지마다 별도의 JS 파일을 만들 필요 없이 하나의 파일로 여러 페이지의 기능을 효율적으로 관리할 수 있음.

'DB' 카테고리의 다른 글

SQL 사용법  (0) 2025.12.10
데이터 베이스 구조  (0) 2025.12.10
쿼리문 명령어  (0) 2025.12.09
MySQL 설치방법  (0) 2025.12.09
기본 개념 잡기  (0) 2025.11.27
'DB' 카테고리의 다른 글
  • 데이터 베이스 구조
  • 쿼리문 명령어
  • MySQL 설치방법
  • 기본 개념 잡기
dev_user
dev_user
csjang94-dev 님의 블로그 입니다.
  • dev_user
    devJang
    dev_user
    • Category (127)
      • Linux (2)
      • FrontEnd (11)
        • HTML (1)
        • CSS (5)
        • JavaScript (4)
        • React (1)
      • BackEnd (4)
        • node.js (7)
        • Vue.js (1)
      • DB (13)
      • Python (3)
      • GitHub (1)
      • AWS (18)
      • ErrorCode (2)
      • AWS 광주 개발일지(25.08.19~25.01... (55)
        • 교육 25년 8월 일지 (7)
        • 교육 25년 9월 일지 (22)
        • 교육 25년 10월 일지 (2)
        • 교육 25년 11월 일지 (4)
        • 교육 25년 12월 일지 (19)
      • Projects (3)
        • 미니프로젝트(25.08.29) (0)
        • Git Page -> Portfolio제작 (2.. (1)
      • 개념 정리 (1)
      • 유용한 서비스 (3)
  • 인기 글

  • 링크

    • GitHub
    • Portfolio
    • YouTube
    • 개발자 로드맵 사이트
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 전체
    오늘
    어제
  • hELLO· Designed By정상우.v4.10.4
dev_user
로그인 기능 구현
상단으로

티스토리툴바