https://www.youtube.com/watch?v=X7q_IYXoyXg
1. 이건 꼭 알아야 한다[^1]
[? 질문] 로그인한 사용자만 특정 API를 호출하게 만들려면 서버는 “누가 로그인 중인지”를 어떻게 판단해야 하는가[^2]
[= 답] 서버 메모리에 로그인 사용자 목록을 들고 있는 stateful 방식도 가능하지만, 확장성과 비용 문제 때문에 일반적으로는 로그인 성공 시 액세스 토큰(Access Token) 을 발급하고 요청마다 그 토큰을 검증하는 stateless 방식으로 인증을 구현한다.[^7]
[? 질문] 액세스 토큰을 클라이언트에 어떻게 전달하고, 서버에서는 어떻게 꺼내서 검증하는가[^12]
[= 답] 이 영상에서는 로그인 성공 시 JWT(JSON Web Token) 로 토큰을 만들고, 이를 쿠키(cookie) 로 내려준다. 이후 보호된 API에서 cookie-parser로 쿠키의 토큰을 읽고, jwt.verify()로 토큰을 복호/검증한 뒤 payload(예: username)로 DB에서 사용자를 조회해 유효 사용자면 통과시킨다.[^13]
[? 질문] 인증 로직을 API마다 반복하지 않고 깔끔하게 재사용하는 방법은 무엇인가[^23]
[= 답] Express의 미들웨어(middleware) 로 인증 과정을 분리하고, 보호가 필요한 라우트에 미들웨어만 끼워 넣어 반복 코드를 제거한다(인증 성공 시 next() 호출).[^^26]
2. 큰 그림[^2]
이 콘텐츠는 웹 프론트엔드 개발자가 백엔드와 협업/개발 시 반드시 이해해야 하는 인증(auth) 의 최소 개념과, Express 기반 서버에서 “로그인한 사용자만 접근 가능한 API”를 구현하는 전체 흐름을 코드 중심으로 설명한다.[^4]
특히 “서버가 로그인 상태를 직접 들고 있어야 하는가?”라는 질문에서 출발해, 토큰 기반 인증(Stateless) 으로 전환하는 이유와 구현 디테일(쿠키 전달, JWT 검증, DB 조회, 예외 처리, 미들웨어화)을 단계적으로 만든다.[^7]
- Stateful vs Stateless: 서버 메모리에 로그인 사용자 목록을 관리하는 방식은 동시 접속자가 늘면 메모리/비용 문제가 커지므로, 토큰으로 상태를 클라이언트에 위임하는 설계를 선택한다.[^6]
- JWT 기반 액세스 토큰 발급/검증: 로그인 성공 시 JWT로 토큰을 만들고, 보호 API 요청 때 쿠키에서 토큰을 읽어
verify후 payload로 DB 사용자 존재 여부를 확인한다.[^18] - 보안과 재사용성: 쿠키에 HttpOnly 옵션을 켜서 JS로 토큰을 못 읽게 하여 탈취 위험을 줄이고, 인증 로직은 Express 미들웨어로 빼서 모든 API에 쉽게 적용한다.[^22]
3. 하나씩 살펴보기[^2]
3.1 목표: “로그인한 사용자만 호출 가능한 테스트 API” 만들기[^3]
화자는 지난 영상에서 회원가입/로그인 구현까지 했고, 이제 로그인 이후에 “로그인한 사람만 사용할 수 있는 기능”을 제공해야 한다고 말한다.[^2]
대표적인 예로 네이버 카페처럼 “로그인했고 권한이 있으면 글이 보이고, 아니면 권한 없음이 뜨는” 상황을 들며, 로그인 유저 정보에 따라 API 동작이 달라지게 만드는 인증 기능을 만들겠다고 한다.[^3]
이번 영상의 구체 목표는 다음과 같다.[^4]
- 테스트용 API 하나를 만든다.[^4]
- 이 API는 허용된 유저(=로그인한 사람) 만 호출할 수 있다.[^4]
- 로그인하지 않았다면 이 API 호출은 실패해야 한다(에러 응답).[^10]
즉, “요청을 보낸 사람이 로그인한 사용자임을 서버가 확인하고, 확인되면 API 실행을 허용”하는 흐름을 구현하는 것이다.[^5]
3.2 서버가 로그인 사용자를 “기억”하는 1번째 방법: 메모리/서버 상태 기반(Stateful)[^6]
화자는 “로그인 증(?)을 구현하려면 서버가 지금 로그인한 사람이 누구인지, DB에 있는 유효한 사용자인지 확인 과정이 필요하다”고 말하며 출발한다.[^5]
그리고 “유효 사용자임을 어떻게 아느냐”에서 가장 먼저 떠오르는 단순한 접근을 제시한다.[^6]
3.2.1 방식 설명: 서버 메모리에 ‘로그인한 사용자 목록’을 저장[^6]
- 클라이언트가 로그인하면 서버가 “지금 누가 로그인했다”를 서버 메모리 어딘가에 기록한다.[^6]
- 이후 클라이언트가 어떤 API를 호출하면, 서버는 먼저 “이 사용자가 메모리에 기록된 로그인 사용자냐”를 체크한다.[^6]
- 메모리에 있으면 API 실행을 허용하고 응답한다.[^6]
- 로그아웃 시에는 메모리에서 해당 사용자를 삭제한다.[^6]
화자는 이를 “첫 번째 방법을 요약하면 서버에서 로그인한 사용자 기록하고 있는 것”이라고 정리한다.[^6]
3.2.2 단점: 동시 접속자가 폭증하면 메모리/비용이 폭증[^7]
화자는 이 방식의 단점을 “동시 접속 규모”로 설명한다.[^7]
- 작은 서비스(유명하지 않아 동시 로그인 사용자가 많지 않음)에서는 메모리 점유가 크지 않아 문제가 없을 수 있다.[^7]
- 하지만 서비스가 커져 “동시에 100만 명씩 접속”하면 로그인 사용자 정보를 담기 위한 메모리 공간이 매우 커져야 한다.[^7]
- 그 결과 서버 컴퓨터 성능을 올리고 메모리를 더 확보해야 한다.[^7]
- 특히 “일시적 이벤트 때만 100만 명이 접속하고 평소엔 적게 접속”하는 서비스라면, 최대치를 대비한 인프라 증설은 낭비가 된다.[^7]
- 그럼에도 stateful로 구현해버리면 최대치 대비를 해야 하니 비싼 컴퓨팅 비용을 지불하게 된다.[^7]
여기서 화자는 “잘 생각해보면 로그인 정보를 서버가 꼭 갖고 있지 않아도 된다”는 결론으로 다음 방식(토큰 기반)으로 넘어간다.[^7]
3.3 2번째 방법: 토큰 기반(Access Token)으로 상태를 클라이언트에 위임(Stateless)[^8]
3.3.1 방식 설명: 로그인 성공 시 액세스 토큰 발급, 요청마다 토큰으로 인증[^8]
화자는 두 번째 방법을 다음과 같이 전개한다.[^8]
- 클라이언트가 서버에 로그인 요청을 보낸다.[^8]
- 서버는 로그인한 사람이 DB의 유효한 사용자인지 확인한다.[^8]
- 유효한 사용자라면 서버는 액세스 토큰(Access Token) 을 발급해준다.[^8]
- 클라이언트는 이 토큰을 저장/보관한다.[^8]
- 이후 어떤 API가 필요할 때, 클라이언트는 서버 요청에 액세스 토큰을 함께 보낸다.[^8]
- 서버는 보호된 API 호출 시, 토큰을 근거로 다시 “유효 사용자”인지 확인한다(토큰 검증 + DB 체크).[^8]
- 유효하면 API 실행, 아니면 “로그인하지 않은 사용자입니다” 같은 에러를 응답한다.[^9]
핵심은 서버가 “누가 로그인 중인지 목록을 관리”하지 않고도, 요청마다 토큰의 유효성 판단만으로 인증을 수행할 수 있다는 점이다.[^10]
3.3.2 Stateful/Stateless 용어 정리(영상 내 정의)[^11]
화자는 여기서 두 용어를 명시적으로 붙인다.[^11]
- 서버가 로그인 사용자 정보를 “관리하고 있는 것” → 스테이트풀(Stateful)[^11]
- 서버는 로그인 사용자 관리 없이 토큰을 발급하고 클라이언트가 들고 있도록 “위임”하는 것 → 스테이트리스(Stateless)[^11]
그리고 이번 영상에서는 Stateless 방식으로 구현하겠다고 선언한다.[^11]
[!IMPORTANT] Stateless의 구현 목표(영상에서의 기준) “로그인한 사람만 API를 호출할 수 있게 만든다”는 목적을, 서버의 ‘로그인 사용자 목록’ 관리 없이 토큰 검증으로만 달성한다.[^11]
3.4 현재 상태 점검: 지금은 인증이 없어서 API가 그냥 호출됨[^11]
화자는 “지금은 인증 관련 기능을 구현하지 않았기 때문에 그냥 호출할 수 있다”고 말하며, 이제 이를 “로그인한 사람만 호출” 가능하도록 바꾸겠다고 한다.[^11]
이를 위해 먼저 “아까 말한 액세스 토큰”을 만들겠다고 진행한다.[^12]
3.5 액세스 토큰은 언제 발급? 로그인 성공 시 발급[^12]
화자는 질문을 던지고 바로 답한다.[^12]
[? 질문] 액세스 토큰은 언제 발급되어야 할까[^12]
[= 답] 로그인에 성공했을 때 발급해야 한다.[^12]
기존에는 로그인 성공 시 “로그인 성공” 메시지를 내려줬지만, 이제부터는 “이 사람이 로그인한 사람임을 인정할 수 있는 액세스 토큰”을 발급해 내려줘야 한다고 설명한다.[^12]
3.6 JWT로 액세스 토큰 발급하기: 라이브러리 설치와 sign 사용[^13]
3.6.1 JWT를 사용하겠다: “JSON Web Token”[^13]
화자는 액세스 토큰을 만들기 위해 JWT(JSON Web Token) 을 사용한다고 말한다.[^13]
사용법 개요로, jwt.sign()에 “토큰에 담을 데이터”와 “암호화 키(시크릿 키)”를 넣으면 암호화된 토큰이 만들어진다고 설명한다.[^13]
3.6.2 설치/require 후 로그인 성공 시점에 토큰 생성[^13]
화자는 JWT 패키지를 설치하고 require로 가져온 뒤, “토큰을 발급해야 할 곳 = 로그인 성공했을 때”이므로 로그인 로직의 성공 구간에 토큰 발급 코드를 넣겠다고 한다.[^13]
처음에는 토큰을 “출력만” 해보며, 로그인 API를 호출했더니 기존 “로그인 성공” 메시지와 함께 콘솔에 찍힌 값이 액세스 토큰임을 확인한다.[^14]
즉 이 단계에서 달성한 것은:
- 로그인 성공 시 서버가 JWT 토큰을 생성한다.[^14]
- 토큰 문자열이 실제로 생성되는 것을 로그로 확인했다.[^14]
3.7 토큰을 클라이언트로 전달하는 2가지 방법, 그중 쿠키로 전달하기[^15]
화자는 생성한 액세스 토큰을 클라이언트로 넘겨야 한다고 말하며 “크게 두 가지 방법”을 제시한다.[^15]
- 쿠키(cookie) 로 넘기는 방법[^15]
- 리스폰스(응답)로 보내는 방법(본문 맥락상 response body/header 등으로 직접 전달)[^15]
이번 영상에서는 먼저 쿠키로 전달을 해본다.[^15]
3.7.1 쿠키에 액세스 토큰 담기: res.cookie(key, value, ...) 형태[^15]
화자는 액세스 토큰을 쿠키에 담으려면 쿠키의 key/value를 설정하면 되고, 그러면 쿠키에 액세스 토큰이 담긴다고 설명한다.[^15]
그리고 다시 로그인 요청을 보내서, 응답(또는 Postman/유사 도구)의 쿠키 영역에서 액세스 토큰 쿠키 값이 들어온 것을 확인한다.[^16]
이로써 “클라이언트에 액세스 토큰이 전달됨”을 확인하고, 다음 단계로 넘어간다.[^16]
3.8 보호 API에서 인증 구현(1): 쿠키에서 액세스 토큰 존재 여부 확인[^17]
이제 목표는 “클라이언트가 API 호출 시 쿠키에 담긴 액세스 토큰을 같이 보내고, 서버는 이를 검사해 인증된 사용자만 접근하도록” 만드는 것이다.[^16]
3.8.1 먼저 할 일: 요청에 토큰이 있는지부터 확인[^17]
화자는 “클라이언트에서 서버로 요청할 때 쿠키에 액세스 토큰을 담아 보낼 것”이므로 서버는 액세스 토큰이 있는지 없는지부터 확인해야 한다고 말한다.[^17]
하지만 Express에서 쿠키를 쉽게 쓰려면 파싱이 필요하므로 cookie-parser 패키지를 설치/등록한다.[^17]
- cookie-parser 설치[^17]
- 미들웨어로 등록[^17]
- 그러면
req.cookies를 사용할 수 있다.[^17]
3.8.2 토큰이 없으면 401 Unauthorized + 메시지[^17]
화자는 액세스 토큰이 없다면 다음처럼 처리한다고 설명한다.[^17]
- 상태코드는 Unauthorized(401) 로 응답한다.[^17]
- 메시지로 “쿠키에 액세스 토큰 없거든요”처럼 토큰이 없음을 알려준다.[^17]
3.8.3 테스트: 쿠키 삭제 후 호출하면 “토큰 없음” 에러가 나야 함[^18]
화자는 실제로 테스트한다.[^18]
- 처음엔 쿠키가 있어 API가 정상 호출된다.[^18]
- 도구의 쿠키 탭에서 쿠키를 삭제한다.[^18]
- 그 후 API를 호출하면 쿠키가 없으므로 “액세스 토큰 없다는 메시지”가 출력된다.[^18]
- 다시 로그인하면 쿠키가 생기고, 호출이 정상 동작한다.[^18]
이 단계에서 확인한 결론은 다음이다.[^18]
- 쿠키 기반으로 토큰을 실어 보내는 흐름이 동작한다.[^18]
- 서버는 “토큰 존재 유무”에 따라 접근을 1차로 차단할 수 있다.[^18]
3.9 보호 API에서 인증 구현(2): “토큰이 있다”만으로는 부족—검증과 사용자 확인 필요[^19]
화자는 “토큰이 있다고 호출하면 될까요? 안되겠죠”라고 말하며, 다음 단계가 필요하다고 한다.[^19]
- 액세스 토큰이 인증된 사용자에 의해 발급된 유효 토큰인지 확인해야 한다.[^19]
- 그리고 그 토큰이 “누구인지” 식별할 수 있어야 한다.[^19]
이를 위해 조건문(검증 로직)을 추가한다.[^19]
3.10 JWT payload에 담긴 정보로 사용자 식별: username을 넣었고, 이를 DB에서 확인[^20]
3.10.1 토큰 안에는 정보가 담겨 있다: 예시로 username[^20]
화자는 “액세스 토큰 안에는 정보가 담겨 있다”고 말하며, 본인이 토큰 생성 시 username 을 넣어뒀다고 설명한다.[^20]
따라서 서버는 토큰에서 username을 얻고, 그 username이 DB에 존재하는지 확인하면 된다고 한다.[^20]
3.10.2 문제: 토큰은 암호화되어 있으니 해제(검증/복호)해야 함 → jwt.verify()[^20]
토큰은 암호화되어 있으므로 이를 해제하는 방법으로 verify 메소드를 사용한다고 안내한다.[^20]
jwt.verify(accessToken, secretKey)형태로 사용[^20]- secret은 토큰을 만들 때 사용한 시크릿을 동일하게 제공해야 한다는 맥락을 준다.[^20]
- verify 결과(디코드된 값)를 출력해보니 payload에 username이 잘 들어있음을 확인한다.[^20]
즉, 이 단계에서 “토큰 문자열 → (검증 후) payload 객체”로 바꾸는 데 성공한다.[^20]
3.11 DB에서 실제 사용자 존재 확인: payload.username으로 조회[^21]
3.11.1 verify로 얻은 decoded에서 username을 꺼냄[^21]
화자는 디코드 결과 안에 username이 있음을 확인했으니, 그 값을 변수로 꺼내서 사용한다고 말한다.[^21]
3.11.2 DB에 해당 username이 있는지 확인(없으면 에러)[^21]
그 다음은 데이터베이스에서 username이 있는지 확인하는 단계다.[^21]
- DB의 user(사용자) 테이블/컬렉션에서 username을 찾아본다.[^21]
- 찾으면 회원가입 때 저장했던 데이터(예: username, password, 나이, 생일 등)가 출력되는 것을 확인한다.[^21]
- 만약 토큰의 username이 DB에 없으면(=유효하지 않거나 더 이상 존재하지 않는 사용자 등) “유효하지 않은 토큰” 같은 에러로 처리해야 한다고 말한다.[^21]
화자는 “DB에서 찾았는데 user가 없다면 토큰이 아니라고 에러를 보내주면 되겠죠”라고 정리하고, 이 검증을 통과하면 “이제 API를 진짜 사용할 수 있게” 된다고 말한다.[^21]
3.12 공격/오류 시나리오 테스트: 임의로 조작한 토큰을 넣으면 verify에서 에러 발생[^22]
화자는 실제로 “임의로 액세스 토큰을 만들어” 쿠키 값을 조작해 테스트한다.[^22]
- payload는 아무거나 넣고(또는 임의 토큰 문자열 생성), 쿠키에 “유효하지 않은 액세스 토큰”을 담는다.[^22]
- 그 상태로 보호 API를 호출해본다.[^22]
- 결과로
jwt malformed에러가 발생한다.[^22] - 이는 시크릿 키로 토큰을 해제(verify)하려는데 토큰 형식/서명이 이상해서 검증이 불가능하다는 의미로 해석한다.[^22]
3.12.1 예외 처리: verify에서 던지는 에러를 try/catch로 통일 처리[^22]
화자는 이 에러도 “유효하지 않은 토큰” 범주에 포함시킬 수 있다고 말한다.[^22]
따라서 verify를 try/catch로 감싸고, 에러가 발생하면 전부 “유효하지 않음/인증 실패”로 처리해 응답하도록 예외 처리를 추가한다.[^22]
- verify 과정 에러 → catch에서 인증 실패 응답[^22]
- DB에서 user가 없는 경우도 동일하게 인증 실패로 throw/처리[^22]
화자는 이렇게 해서 인증 기능을 구현할 수 있다고 결론낸다.[^22]
3.13 쿠키 보안 옵션: HttpOnly를 켜서 JS에서 토큰을 못 읽게 하기[^23]
화자는 쿠키를 사용할 때 httpOnly 옵션을 소개한다.[^23]
[? 질문] 액세스 토큰을 발급한 뒤, 클라이언트에서 이 토큰을 “읽을” 일이 있을까[^23]
[= 답] 필요 없다. 클라이언트는 쿠키를 가진 채로 API 요청에 담아 보내기만 하면 된다.[^23]
즉 프론트엔드 JS 코드가 액세스 토큰을 읽거나 조작할 필요가 없다는 전제를 둔다.[^23]
그리고 “클라이언트에서 JS로 쿠키를 해체해서 사용”하는 건 보안상 문제가 될 수 있다고 설명한다.[^23]
3.13.1 위험 예시(XSS 관점의 설명): 악성 스크립트가 토큰을 탈취할 수 있음[^23]
화자는 예시로, 해커가 악성 코드를 심어 사용자가 가진 액세스 토큰을 해커에게 전송하게 만들 수 있다고 말한다.[^23]
즉 “클라이언트에서 토큰을 읽을 수 있게 열어두면” 탈취 코드가 가능하다는 문제의식을 제시한다.[^23]
3.13.2 해결: HttpOnly=true로 설정[^24]
그래서 “클라이언트에서 액세스 토큰을 읽을 수 없게 하는 옵션”이 HttpOnly 라고 설명한다.[^24]
이 옵션을 true로 주면:
- 쿠키는 서버와 통신 시에만 실려가고[^24]
- 클라이언트에서 토큰을 열어서 뭔가 하는 것은 못하게 된다[^24]
- 결과적으로 보안상 더 좋다[^24]
설정은 쿠키를 담을 때 옵션으로 추가하며, 다시 발급 후 쿠키를 확인하면 HttpOnly가 켜져 있는 것을 확인한다.[^24]
3.14 확장 문제: 보호 API가 100개면 인증 코드도 100번 반복? → 미들웨어로 분리[^25]
화자는 방금 만든 인증 로직을 “API마다 다 넣어야 하느냐”라는 확장 문제를 제기한다.[^25]
- 만약 API가 100개라면, 매 API마다 액세스 토큰 검증 코드를 반복해야 한다.[^25]
- 이는 너무 귀찮고 코드도 지저분해진다.[^25]
- 그래서 반복 로직을 함수로 빼고 싶어질 텐데, Express에는 이를 위한 미들웨어 개념이 있다고 소개한다.[^25]
즉, “인증/인가 같은 반복 동작을 미들웨어로 빼면 더 깔끔하게 작성 가능”하다는 흐름이다.[^25]
3.15 Express 미들웨어로 인증 로직 분리: validateUser 미들웨어와 next()[^26]
3.15.1 미들웨어 폴더/파일 생성 후 인증 코드 이동[^26]
화자는 middleware 폴더를 만들고, 예를 들어 validateUser 같은 파일(코드)을 만든 뒤 기존에 라우트에 있던 인증 로직을 그대로 복사해 넣는다.[^26]
그리고 “반복되는 로직은 어떤 거냐면, (실제 API 동작 코드 부분을 제외한) 나머지”라고 말하며 인증/예외처리 블록이 반복 대상임을 강조한다.[^26]
3.15.2 핵심: 인증 통과 후 다음 처리로 넘기려면 next() 호출[^26]
미들웨어에서 인증 로직을 실행한 다음, 인증이 끝났을 때 “원래 이 API에서 실행돼야 할 코드(실제 비즈니스 로직)”가 실행되게 하려면 다음 단계로 넘겨야 한다.[^26]
그때 Express 미들웨어의 핵심 키워드인 next()를 호출하면 된다고 설명한다.[^26]
즉 흐름은 다음처럼 재구성된다.[^26]
- 요청이 들어오면 validateUser 미들웨어가 먼저 실행된다.[^26]
- 쿠키 토큰 확인 → verify → DB 조회 → 예외처리까지 모두 수행한다.[^26]
- 통과하면
next()로 다음 핸들러(실제 API 로직)로 넘긴다.[^26] - 실패하면 그 자리에서 에러 응답으로 종료한다.[^26]
3.15.3 라우트에 미들웨어 붙이기: 두 번째 인자로 추가[^27]
화자는 API에서 미들웨어를 사용하는 방법은 “미들웨어를 가져오고, 라우트 선언 시 두 번째 인자(또는 중간 인자)로 추가”하는 것이라고 설명한다.[^27]
이렇게 하면 기존 라우트의 반복 인증 코드는 제거되고, 미들웨어를 재사용해 동일하게 동작한다는 것을 테스트로 확인한다.[^27]
또한 DB 객체/연결 같은 것을 미들웨어 파일에서 어떻게 가져올지(바깥으로 빼서 사용)도 정리하며, 최종적으로 “미들웨어로 빼내도 잘 작동”함을 확인한다.[^27]
3.16 결과: 새 보호 API 추가 시 미들웨어만 끼우면 동일 인증이 적용됨[^28]
화자는 이제 새로운 API를 만들 때, 매번 복잡한 인증 코드를 작성하는 대신 “미들웨어만 붙여주면 똑같이 동작”한다고 말한다.[^28]
그리고 이 방식이 “굉장히 좋다/수월하다”는 맥락으로 마무리한다.[^28]
마지막으로 이번 영상에서 한 일을 다시 말한다.[^29]
- 액세스 토큰을 발급했다.[^29]
- 클라이언트에 전달했다(이번 편은 쿠키 사용).[^29]
- 클라이언트가 요청 시 쿠키로 보내는 토큰을 서버가 검증해 “인증된 사용자만 API 호출 가능”하게 했다.[^29]
3.17 다음 예고: 쿠키 말고 토큰을 직접 반환하고, 클라이언트 저장소(로컬스토리지 등) 활용 방식[^30]
화자는 다음 영상에서는 쿠키가 아닌 방식—즉 액세스 토큰 자체를 클라이언트에 반환하고, 클라이언트가 이를 저장했다가 보내는 방식—을 다룰 것이라고 예고한다.[^30]
예로 로컬 스토리지(LocalStorage) 저장 등 다양한 방법이 있다고 언급하며 영상이 끝난다.[^30]
4. 핵심 통찰[^2]
- [h “로그인 상태”를 서버 메모리에 저장하는 단순한 구현은 확장성과 비용에서 한계가 빠르게 온다.] 동시 접속이 폭증하는 순간 메모리/인프라 비용을 최대치에 맞춰야 하기 때문이다.[^7]
- [c Stateless 인증의 본질은 ‘서버가 로그인 사용자 목록을 관리하지 않고도’ 요청마다 토큰 검증으로 사용자 인증을 끝내는 것이다.] 서버는 토큰 검증과 DB 확인으로 “유효 사용자”만 판단하면 된다.[^10]
- 실행: 로그인 성공 시 토큰 발급, 보호 API에서 토큰 유무/유효성/사용자 존재 확인을 표준 플로우로 만든다.[^18]
- [h “토큰이 있다”는 사실만으로는 인증이 아니다.] 토큰의 위조/변조 가능성을 고려해
jwt.verify()같은 검증이 필요하고, payload로 DB 사용자 조회까지 해야 안전한 인증 흐름이 된다.[^19] - [h 쿠키 기반 전달을 택한다면 HttpOnly는 ‘프론트 JS에서 토큰을 못 읽게’ 만들어 XSS 기반 탈취 위험을 줄이는 중요한 옵션이다.] 클라이언트는 읽지 않고 자동 전송만 하면 된다는 전제에서 특히 유효하다.[^24]
- 실행:
res.cookie(..., { httpOnly: true })로 발급하고, 클라이언트에서 토큰 접근이 필요 없는 구조로 설계한다.[^24]
- 실행:
- [h 인증 로직은 API마다 반복 작성하면 유지보수가 급격히 나빠진다.] Express 미들웨어로 빼고
next()로 통과시키는 구조가 재사용성과 일관성을 만든다.[^26]- 실행:
validateUser같은 미들웨어 파일로 분리하고, 보호 라우트에 공통으로 장착한다.[^27]
- 실행:
5. 헷갈리는 용어 정리[^11]
액세스 토큰(Access Token): 로그인 성공 후 서버가 발급해 클라이언트가 보관하는 토큰으로, 이후 API 요청 시 함께 보내 인증에 사용한다.[^8]
JWT(JSON Web Token): 토큰을 생성/서명하고(sign), 요청 시 검증/해제(verify)해 payload를 얻는 데 사용하는 토큰 포맷/기술로 소개된다.[^13]
Stateful: 서버가 “로그인한 사용자” 정보를 직접 저장/관리하는 방식(예: 서버 메모리에 로그인 유저 목록 유지).[^11]
Stateless: 서버가 로그인 사용자 목록을 관리하지 않고, 토큰을 발급해 클라이언트가 들고 있게 하며 요청마다 토큰 유효성으로 인증하는 방식.[^11]
cookie-parser: Express에서 요청 쿠키를 파싱해 req.cookies로 쉽게 접근하게 해주는 미들웨어 패키지.[^17]
HttpOnly 쿠키 옵션: 쿠키를 클라이언트 자바스크립트에서 읽지 못하게 하여(서버 통신에만 사용) 토큰 탈취 위험을 줄이는 설정.[^24]
미들웨어(Middleware): Express에서 요청-응답 사이에 공통 로직(인증 등)을 분리해 재사용하는 구성 요소로, 통과 시 next()로 다음 핸들러를 실행한다.[^26]
참고(콘텐츠 정보)[^1]
- 제목: 웹 프론트엔드 개발자가 알아야할 최소한의 백엔드 지식과 코드 (auth)[^1]
- 채널: 라매개발자[^1]
- 길이: 21분 35초[^1]
- 링크: https://www.youtube.com/watch?v=X7q_IYXoyXg[^1]
[^1]: 유튜브 메타/사용자 제공 정보: "웹 프론트엔드 개발자가 알아야할 최소한의 백엔드 지식과 코드 (auth)", 채널 "라매개발자", 길이 "21분 35초", https://www.youtube.com/watch?v=X7q_IYXoyXg
[^2]: @[00:02] "지난 영상에서 회원가입과 로그인 기능 구현 하는 것까지 해봤는데요… 이제 로그인을 했으면…"
[^3]: @[00:13] "그것 중에 가장 대표적인게 인증 기능 입니다… 네이버 카페…"
[^4]: @[00:44] "허용하는 유저만 로그인 한 사람만 호출할 수 있고 로그인 하지 않았다면 호출할 수 없는…"
[^5]: @[01:00] "…서버에서…누구인지…유효한 사용자 인지 확인 과정이 필요합니다"
[^6]: @[01:17] "…클라이언트가 로그인을 하면 서버에 메모리에다가…추가… 메모리에 저장… 체크…"
[^7]: @[02:12] "~@[03:20] 동시접속 100만명, 메모리/성능/비용, 이벤트성 트래픽 낭비, 서버가 로그인 정보를 꼭 갖고 있지 않아도 됨"
[^8]: @[03:30] "…유효한 사용자라면 엑세스 토큰… 발급… api 필요할 때… 토큰이랑 같이 요청"
[^9]: @[04:21] "로그인하지 않은 사용자입니다… 에러 응답"
[^10]: @[04:32] "…서버가…몇 명인지…관리할 필요가 없습니다… 액세스 토큰이 유효한…"
[^11]: @[04:45] "…스테이트 풀… 스테이트 리스… 저희는… 구현"
[^12]: @[05:26] "…액세스 토큰… 언제 발급… 로그인을 했을 때…"
[^13]: @[05:48] "…제이슨 웹 토큰… jwt… sign… 데이터… 키값…"
[^14]: @[07:02] "…로그인 api… 로그인 성공… 콘솔 로그 때문에… 액세스 토큰"
[^15]: @[07:26] "넘겨주는 방법… 쿠키… 리스폰스로… 먼저 쿠키…"
[^16]: @[07:59] "…쿠키… 엑세스 토큰 값이 잘 들어와…"
[^17]: @[08:42] "…쿠키… 액세스 토큰 있는지… cookie-parser 설치… req.cookies… 없으면 unauthorized…"
[^18]: @[09:49] "~@[10:28] 쿠키 삭제 후 호출 시 토큰 없음 메시지, 다시 로그인 후 정상 호출"
[^19]: @[10:28] "x 토큰이 있다고 호출을 하면 될까요… 안되겠죠… 인증된 사용자 인지… 누구인지…"
[^20]: @[10:49] "~@[11:51] 토큰 안에 username 넣음, 암호화 해제는 verify, decoded에 username 확인"
[^21]: @[11:59] "~@[13:34] decoded.username으로 DB 조회, 사용자 데이터 출력, 없으면 유효하지 않은 토큰 에러"
[^22]: @[13:44] "~@[15:09] 임의 토큰 넣기→jwt malformed, try/catch로 예외 처리, 유효하지 않은 토큰 처리"
[^23]: @[15:16] "~@[15:58] httpOnly 옵션 소개, 클라이언트가 토큰 읽을 필요 없음, XSS로 탈취 가능성 예시"
[^24]: @[16:05] "~@[16:48] httpOnly=true 설정, 쿠키에서 httpOnly 확인, 보안상 이점"
[^25]: @[16:50] "~@[17:14] API 100개면 반복, 함수로 빼고 싶음, Express 미들웨어 소개"
[^26]: @[17:30] "~@[18:43] middleware 폴더/파일, 반복 로직 분리, 인증 후 next() 호출로 다음 API 실행"
[^27]: @[18:51] "~@[20:10] 라우트에 미들웨어 추가(두 번째 인자), DB 가져오기 정리, 동작 확인"
[^28]: @[20:17] "~@[20:41] 새 API 만들 때 인증 코드 대신 미들웨어만 붙이면 동일 동작"
[^29]: @[20:41] "~@[21:07] 액세스 토큰 발급→전달→인증된 사용자만 API 호출 기능 구현, 쿠키 사용"
[^30]: @[21:07] "~@[21:23] 다음 영상 예고: 쿠키 말고 토큰 직접 반환, 로컬스토리지 등 저장 방식"