본문 바로가기
프로그래밍

인증, 권한 관리

by 로맨틱스터디 2025. 10. 10.
728x90
반응형

백엔드 구현에서 **인증(Authentication)**과 **권한 관리(Authorization)**는 핵심 중 핵심이에요.
단계별로 정리하면서 실무에서 어떻게 구현하는지 차근차근 설명할게요.


1️⃣ 인증(Authentication) – 누가 접근하는지 확인

🔹 목적

  • 사용자가 누구인지 확인
  • 로그인, 세션, 토큰 등을 통해 신원을 검증

🔹 구현 방법

방법 설명 특징
세션 기반 인증
(Session-based)
서버가 로그인 세션을 관리
쿠키에 세션 ID 저장
서버 메모리/DB상태 저장 필요
토큰 기반 인증
(Token-based, JWT)
로그인 시 서버가 JWT(JSON Web Token) 발급
클라이언트가 매 요청마다 전송
서버는 상태 저장 불필요, 분산 환경 유리
OAuth / Social Login 구글, 카카오, 페이스북 등 외부 인증 사용 소셜 계정으로 로그인 가능

🔹 예시: JWT 인증 흐름

  1. 클라이언트 → 로그인 정보 제출
  2. 서버 → 사용자 확인 후 JWT 생성
  3. 클라이언트 → 요청 시 JWT 헤더(Authorization: Bearer <token>)에 포함
  4. 서버 → JWT 검증 후 요청 처리

핵심: 인증은 “누구인지 확인”하는 과정

 

 

Q. Authorization 헤더 유무

더보기

1️⃣ Authorization 헤더란?

  • Authorization 헤더 = 클라이언트가 서버에 “나는 누구다”를 증명하는 정보를 담는 곳
  • 보통 형식:
 
Authorization: Bearer <JWT 토큰>
  • 서버는 이 헤더를 보고 사용자를 인증(Authentication) 합니다.

2️⃣ 헤더가 없을 때 의미

  • 헤더가 없다고 무조건 인증이 필요 없는 요청은 아님
  • 의미는 클라이언트가 인증 정보를 보내지 않았다는 것

상황별 의미

경우의미처리 방법
공개 API 누구나 접근 가능 헤더 없어도 OK
인증 필요 API 헤더 없음 401 Unauthorized → 인증 필요
선택적 인증 헤더 있으면 인증 처리, 없으면 기본값 예: 로그인 여부에 따라 동작 분기

즉, 헤더가 없으면 서버가 요청한 API에 따라 달라짐


3️⃣ 실무 예시 (Express.js + JWT)

 
app.get('/public', (req, res) => { res.send('누구나 볼 수 있음'); // Authorization 없어도 OK }); app.get('/private', authMiddleware, (req, res) => { res.send(`안녕하세요 ${req.user.name}`); // Authorization 없으면 401 });
  • /public → 인증 필요 없음, 헤더 없어도 요청 처리
  • /private → 인증 필요, 헤더 없으면 실패

4️⃣ 핵심 요약

  1. Authorization 헤더 = 사용자 인증 정보
  2. 없으면 인증 안 함이 아니라 서버가 요구하는 API에 따라 다름
    • 공개 API → 헤더 없어도 OK
    • 인증 필요 API → 헤더 없으면 401 Unauthorized
  3. 선택적 인증 가능 → 로그인 상태 확인용

💡 실무 팁:

  • API 설계 시 각 엔드포인트마다 인증 필요 여부 명시
  • 미들웨어에서 헤더 유무 + 유효성 검사 한 번에 처리

 

Q. 토큰 방식 종류

더보기

1️⃣ 토큰 기반 인증(Token-based Authentication)이란?

  • 로그인 후 서버가 발급한 토큰을 클라이언트가 매 요청마다 보내는 방식
  • 서버는 토큰을 검증해서 사용자 인증 수행
  • 특징: 서버 상태를 유지하지 않아도 됨 → stateless

2️⃣ 토큰 방식 종류

토큰 방식설명특징
JWT (JSON Web Token) 표준 규격, Payload에 사용자 정보 담아서 서명(Signature) 많이 사용됨, 상태 비저장, 분산 서버에 유리
Opaque Token (불투명 토큰) 서버가 발급하고 DB/Redis에서 토큰-사용자 매핑 관리 서버에서 상태 관리 필요, JWT보다 안전성 높음(정보 노출 없음)
Session Token 전통적 세션 기반 토큰, 세션 ID를 발급 서버 상태 유지 필요, 토큰 자체에 정보 없음

결론: JWT가 토큰 방식에서 가장 많이 쓰이는 편이지만 유일한 방식은 아님.
요구사항에 따라 Opaque Token, Session Token 등 다른 방식도 충분히 가능


3️⃣ JWT가 많이 쓰이는 이유

  1. 표준화 (RFC 7519)
  2. Stateless → 서버가 토큰 상태를 유지하지 않아도 됨
  3. 분산 시스템/마이크로서비스에서 편리
  4. Payload에 정보 담기 가능 → DB 조회 없이 인증 가능

4️⃣ JWT 한계

  • 토큰 만료 전까지 취소가 어려움 (로그아웃 처리 어려움)
  • Payload에 민감한 정보 넣으면 안됨 (암호화 필요)
  • 길이가 길어서 요청 헤더에 부담될 수 있음

5️⃣ 실무 선택 기준

조건추천 방식
단일 서버, 상태 유지 OK Session Token
분산 서버, stateless JWT
보안 최우선, 토큰 유효 기간 길이 필요 Opaque Token + DB/Redis 검증

✅ 정리

  • 토큰 방식 = 서버 상태를 저장하지 않고 토큰으로 인증 처리
  • JWT = 토큰 방식에서 가장 널리 쓰이는 표준
  • 하지만 JWT만 있는 건 아님 → 상황과 요구사항에 맞춰 선택 가능
  • 실무에서는 JWT + Refresh Token 조합이 흔함

 

Q. 로그인을 하고 나면, 서버가 세션 ID를 생성하고, 그 세션 ID를 쿠키에 저장한다는 거지?

이 쿠키는 영속 쿠키야, 세션 쿠키야? 브라우저 종료하면 사라지는 거야 유효기간까지 계속 있는거야?

그리고 서버 메모리/DB에 상태 저장이 왜 필요해? 어떤 상태 말하는 거임? 로그인 성공/실패?

더보기

1️⃣ 세션(Session) 기반 인증 기본 구조

  1. 사용자가 로그인 시도 → 서버가 사용자 인증
  2. 로그인 성공 → 서버가 세션 ID(Session ID) 생성
  3. 서버가 세션 저장소(메모리, DB, Redis 등)에 세션 ID와 사용자 정보를 저장
  4. 서버가 클라이언트(브라우저)에게 쿠키로 세션 ID 전달
  5. 이후 요청마다 쿠키에 있는 세션 ID를 서버에 보내서 인증 확인
 
클라이언트 쿠키: sessionId=abcd1234 서버 세션 저장소: { abcd1234: { userId: 5, role: 'admin' } }

2️⃣ 세션 쿠키 vs 영속 쿠키

  • 세션 쿠키(Session Cookie)
    • 브라우저 종료 시 자동 삭제
    • 쿠키에 Expires/Max-Age가 지정되지 않은 경우
    • 대부분 로그인 세션 쿠키는 기본적으로 세션 쿠키
  • 영속 쿠키(Persistent Cookie)
    • 유효기간(Expires 또는 Max-Age)이 지정되어 있음
    • 브라우저 종료 후에도 남아 있음 → “로그인 유지” 등

즉, 세션 기반 로그인 쿠키는 보통 세션 쿠키로 설정
필요하면 “로그인 유지” 옵션으로 영속 쿠키로 만들 수도 있음


3️⃣ 서버에 상태를 저장하는 이유

  • 서버가 세션 ID만으로 누가 로그인했는지 알 수 있어야 하기 때문
  • 세션 저장소에 기록되는 상태(State):
상태설명
userId / role 로그인한 사용자 정보
로그인 시간, 만료 시간 세션 만료 확인
CSRF 토큰 등 보안 관련 상태
기타 임시 정보 예: 쇼핑카트, 진행 중인 단계 등
  • 로그인 성공/실패만 저장하는 게 아님
  • 로그인 성공 후 “누가 로그인했는지, 어떤 권한인지”를 유지하기 위해 필요

4️⃣ 요약 흐름

  1. 로그인 성공 → 서버: 세션 ID 생성 + 상태 저장
  2. 클라이언트: 세션 ID 쿠키 저장 (세션/영속)
  3. 요청 시 쿠키 전송 → 서버: 세션 ID 조회 → 사용자 인증/권한 확인
  4. 로그아웃 / 세션 만료 → 서버에서 상태 삭제

💡 한눈에 비교: 세션 vs 토큰 기반

항목세션(Session)토큰(JWT)
상태 저장 서버(메모리/DB/Redis) 서버 상태 저장 불필요(stateless)
쿠키 세션 쿠키 / 영속 쿠키 보통 Authorization 헤더
장점 서버에서 제어 가능, 즉시 로그아웃 가능 분산 환경 용이, DB 조회 최소화
단점 서버 메모리 사용, 확장 어려움 토큰 탈취 시 즉시 차단 어려움

 

Q. OAuth 란? Social Login과 같은 것인가?

더보기

OAuth는 백엔드, 인증/소셜 로그인에서 핵심 개념이에요. 차근차근 설명할게요.


1️⃣ OAuth란?

  • OAuth = Open Authorization의 약자
    • 직역하면 “열린 권한 부여”
  • 목적: 사용자 비밀번호를 직접 주고받지 않고, 서비스끼리 안전하게 권한을 위임하는 표준 프로토콜

즉, “내 비밀번호를 다른 서비스에 주지 않고, 다른 서비스가 내 데이터 일부를 사용하게 허락”하는 방법


2️⃣ OAuth 흐름 (간단하게)

예: GitHub 계정으로 로그인

  1. 사용자 → 애플리케이션 “GitHub로 로그인” 클릭
  2. 애플리케이션 → GitHub에 권한 요청
  3. GitHub → 사용자에게 “이 앱이 내 이메일, 이름 사용해도 되나요?” 동의 화면
  4. 동의 후 GitHub → 애플리케이션에 Authorization Code 전달
  5. 애플리케이션 → GitHub 서버에 Code를 Access Token으로 교환 요청
  6. GitHub → Access Token 발급 → 애플리케이션 사용
  7. 애플리케이션 → Access Token으로 사용자 정보 조회 후 로그인 처리

3️⃣ Social Login과의 관계

  • Social Login = OAuth 기반 로그인
    • 구글, 카카오, 네이버, 페이스북 등 외부 인증 서비스 사용
  • Social Login = OAuth 2.0 프로토콜을 실제 서비스에 적용한 사례라고 보면 됨

즉, Social Login은 OAuth의 응용이고, OAuth 자체는 범용 인증/권한 위임 프로토콜


4️⃣ OAuth 특징

특징설명
비밀번호 노출 없음 앱이 비밀번호를 직접 받지 않음
Access Token 사용 권한 범위를 제한 가능
Scope 지정 가능 이메일, 이름, 친구목록 등 접근 권한 제한 가능
표준화 서비스 간 일관된 인증/권한 위임 가능

5️⃣ OAuth vs JWT vs 세션

항목OAuthJWTSession
목적 서비스 간 권한 위임 인증 정보 안전하게 전달 서버에서 로그인 상태 유지
데이터 Access Token 토큰 안에 사용자 정보 서버 저장
특징 Social Login, API 접근 허용 Stateless 인증 서버 상태 필요

✅ 요약

  1. OAuth = Open Authorization
  2. 목적: 비밀번호 없이 안전하게 다른 서비스에 권한 위임
  3. Social Login = OAuth를 사용한 실제 로그인 구현
  4. 핵심 요소: Authorization Code → Access Token → 사용자 정보 조회

 

Q. Authorization: Bearer <token>에서 Bearer가 뭐야

더보기

1️⃣ Authorization 헤더 구조

 
Authorization: Bearer <token>
  • Authorization → HTTP 요청 헤더, 서버에 인증 정보 전달
  • Bearer → 토큰 타입을 나타내는 키워드
  • <token> → 실제 인증 토큰(JWT 등)

즉, 헤더 전체 의미:

“나는 Bearer 토큰 방식으로 인증할 테니, 서버는 이 토큰을 검증해 주세요.”


2️⃣ Bearer가 의미하는 것

  • Bearer Token = “소지자(token을 가진 사람)에게 접근 권한이 있다”라는 의미
  • 즉, 토큰 자체가 권한 증명 수단
  • 서버 입장에서는 토큰을 가지고 있으면 누구인지 확인 가능

예: “토큰 가진 사람이 바로 권한을 가진 사람이다”
→ 토큰 자체가 증명서 역할


3️⃣ Bearer Token 특징

항목설명
소지자 인증 토큰만 있으면 인증 가능, 별도 비밀번호 필요 없음
단순 사용 Authorization 헤더에 Bearer <token>만 넣으면 됨
보안 주의 탈취되면 누구나 사용 가능 → HTTPS 필수
사용 범위 OAuth2, JWT, API 인증 등에서 표준

4️⃣ 다른 Authorization 방식 예시

타입예시특징
Basic Authorization: Basic <base64(username:password)> 아이디/비밀번호 직접 전송 (암호화 필요)
Bearer Authorization: Bearer <token> 토큰 기반 인증, 현재 표준
Digest Authorization: Digest ... HTTP Digest 인증, 보안 강화

요즘 API/백엔드 인증에서는 거의 대부분 Bearer 토큰 사용


✅ 요약

  1. Authorization 헤더 = 인증 정보 전달
  2. Bearer = “토큰 소지자가 접근 권한을 가진다”를 나타내는 토큰 타입
  3. <token> = 실제 인증 토큰(JWT 등)
  4. Bearer Token은 표준화된 API 인증 방식으로, HTTPS와 함께 사용해야 안전

2️⃣ 권한 관리(Authorization) – 무엇할 수 있는지 확인

🔹 목적

  • 사용자가 특정 자원/기능에 접근할 수 있는지 검증
  • 예: 일반 유저는 글 삭제 불가, 관리자만 삭제 가능

🔹 접근 방식

방식 설명
Role-based Access Control (RBAC) 사용자에게 역할(Role) 부여 / 역할에 권한 매핑
Attribute-based Access Control (ABAC) 사용자 속성, 리소스 속성, 환경 조건을 기준으로 접근 제어
Permission 기반 세부 권한 단위(Permission) 직접 부여

🔹 예시: RBAC

  • Roles: admin, user
  • Permissions: create_post, delete_post, read_post
  • 정책:
Role Permissions
admin create, read, update, delete
user create, read
  • 서버에서 요청 처리 시:
 
if (!req.user.roles.includes('admin')) {
  return res.status(403).json({ message: "권한 없음" });
}

핵심: 권한 관리는 “누가 무엇을 할 수 있는지” 검증

 

 

Q. 서버에서 요청 처리 시 코드에 대한 설명

더보기

💡 전체 코드

 
if (!req.user.roles.includes('admin')) { return res.status(403).json({ message: "권한 없음" }); }

이건 “관리자(admin) 권한이 없는 사용자면 접근 거부(403)” 하는 코드예요.


1️⃣ req 객체 — 요청(Request)

  • req = 요청 객체(Request object)
  • Express.js에서 클라이언트가 보낸 요청 정보를 담고 있음.

예:

 
app.get('/users', (req, res) => { ... })

여기서 req 안에는 이런 정보들이 들어 있어요 👇

속성예시설명
req.params { id: 3 } URL 경로 파라미터
req.query { page: 2 } 쿼리스트링(?page=2)
req.body { name: "Tom" } 요청 본문 (POST/PUT)
req.headers { authorization: "Bearer ..." } 요청 헤더
req.user { id: 1, roles: ["user", "admin"] } 인증 미들웨어가 추가한 사용자 정보

⚠️ req.user는 Express 기본 제공 속성이 아니에요.
JWT나 세션 인증을 처리하는 미들웨어가 직접 추가한 사용자 정보예요.
그래서 로그인한 사용자의 데이터가 들어가 있는 거죠.


2️⃣ req.user.roles와 includes()

예를 들어 미들웨어가 이렇게 넣었다고 가정해요:

 
req.user = { id: 1, name: "홍길동", roles: ["user"] };
  • req.user.roles는 ["user"]라는 배열이에요.
  • includes('admin')은 배열 안에 "admin"이 포함되어 있는지 확인하는 배열 메서드예요.
 
["user", "admin"].includes("admin"); // true ["user"].includes("admin"); // false

즉,

 
if (!req.user.roles.includes('admin')) { ... }

user의 roles 배열에 "admin"이 없으면 (관리자 아님)
403 Forbidden 응답을 보내라는 뜻이에요.


3️⃣ res 객체 — 응답(Response)

  • res = 응답 객체(Response object)
  • 서버가 클라이언트에게 보낼 응답을 다루는 객체예요.

res.status(403)

  • HTTP 응답 상태 코드를 설정하는 메서드
  • 403은 Forbidden (권한 없음)
코드의미
200 성공
201 생성됨
400 잘못된 요청
401 인증 실패 (로그인 필요)
403 권한 없음 (로그인했지만 접근 금지)
404 찾을 수 없음

즉,

 
res.status(403)

→ “HTTP 상태 코드 403으로 응답을 준비하겠다”는 뜻이에요.


res.json({ message: "권한 없음" })

  • json()은 JSON 형식의 응답 본문을 보낸다는 뜻.
  • 안에 들어가는 { message: "권한 없음" }은 JS 객체예요.
    • Express가 이걸 JSON 문자열({"message": "권한 없음"})로 변환해서 클라이언트로 보냄.

따라서 클라이언트가 받는 실제 응답은:

 
HTTP 403 Forbidden { "message": "권한 없음" }

✅ 전체 동작 정리

  1. req.user → 로그인된 사용자 정보
  2. req.user.roles.includes('admin') → 관리자 권한인지 확인
  3. 조건에 안 맞으면→ 403 상태 코드와 JSON 메시지 반환
  4.  
    return res.status(403).json({ message: "권한 없음" });

🧠 한 줄 요약

이 코드는 “현재 요청한 사용자의 역할 배열(roles)에 ‘admin’이 포함되어 있는지 확인하고,
없으면 HTTP 403 응답을 JSON 형태로 돌려주는 권한 검사 로직”이에요.

 

Q. 요약

더보기

🌐 상황부터 먼저

지금 이 코드는 **“이 사용자가 관리자(admin)인지 확인하는 문”**이에요.
관리자만 접근할 수 있는 페이지나 API(예: /admin/dashboard) 같은 데서 쓰는 거죠.


🧩 코드 한 줄씩 말로 풀면

 
if (!req.user.roles.includes('admin')) {
  • req는 요청(request)을 담고 있는 객체예요.
    → 클라이언트가 서버로 보낸 내용(주소, 바디, 헤더 등)이 들어있어요.
  • req.user는 **“로그인한 사용자 정보”**를 담은 객체예요.
    → 예: { id: 1, name: "홍길동", roles: ["user"] }
    → 로그인할 때 JWT나 세션 인증 미들웨어가 req.user를 만들어 넣어요.
  • req.user.roles는 **그 사용자의 역할 목록(배열)**이에요.
    → 예: ["user", "admin"] 처럼 여러 개 들어있을 수도 있어요.
  • .includes('admin')은 roles 배열 안에 "admin"이라는 값이 들어있는지 확인하는 함수예요.
    → ["user", "admin"].includes("admin") → true
    → ["user"].includes("admin") → false
  • !는 “아니다”를 의미하는 부정 연산자예요.
    → “admin이 roles에 없다면”이 되는 거예요.

 
return res.status(403).json({ message: "권한 없음" });
  • res는 응답(response) 객체예요.
    → 서버가 클라이언트에게 “응답”을 보낼 때 쓰는 도구예요.
  • res.status(403)은 **응답 상태 코드 403 (Forbidden)**을 지정하는 거예요.
    → “너 로그인은 했지만, 이건 접근하면 안 돼.” 라는 뜻이에요.
  • .json({ message: "권한 없음" })은 응답 본문을 JSON으로 보내는 것이에요.
    → { message: "권한 없음" }은 JS 객체인데,
    Express가 자동으로 JSON 문자열로 바꿔서 보냄.
    → 실제 클라이언트가 받는 건 {"message":"권한 없음"} 이거예요.
  • return은 더 이상 아래 코드 실행하지 말고 응답 보내고 끝내라는 뜻이에요.

📦 요약해서 한 문장으로

로그인한 사용자의 역할 목록에 "admin"이 없으면,
서버는 403 상태 코드와 "권한 없음" 메시지를 JSON 형태로 응답한다.


🔁 비유로 쉽게

  • req.user는 “이 요청을 보낸 사람이 누구인지”를 담은 신분증이에요.
  • roles는 그 사람이 가진 “직급 목록”이에요. (예: 일반회원, 관리자 등)
  • includes('admin')은 “이 사람 직급 중에 관리자 있냐?” 물어보는 거예요.
  • 없으면 → “넌 관리자 아니니까 못 들어와” 하고 403 돌려보내는 거예요.

3️⃣ 백엔드 설계 시 고려 사항

  1. 데이터 구조 설계
    • User 테이블: id, email, password, role, status
    • Token 관리(세션/refresh token) 필요 시 별도 테이블
  2. API 설계
    • 인증 필요 API / 공개 API 구분
    • 권한별 접근 가능 여부 명세
  3. 미들웨어/Interceptor 활용
    • 요청 전 JWT 검증, Role 검증
    • 코드 중복 최소화, 안전하게 통제
  4. 보안 추가
    • 비밀번호는 반드시 해시화(Bcrypt 등)
    • HTTPS 적용, 토큰 유효기간 관리, CSRF/XSS 방어

4️⃣ 정리

단계 역할  
인증(Authentication) “누구인지” 확인 로그인/토큰
권한 관리(Authorization) “무엇을 할 수 있는지” 검증 Role/Permission
데이터 구조 설계 User 테이블 Role, Token 등 포함
구현 포인트 미들웨어, JWT, 세션, API 접근 제어, 비밀번호 암호화  

 

728x90

 

728x90
반응형