인증(Authentication)과 인가(Authorization)
Series: 인증과 인가
들어가며
웹 서비스를 개발하다 보면 자연스럽게 두 가지 질문을 마주하게 된다. 하나는 "이 사용자는 누구인가?"이고, 다른 하나는 "이 사용자가 이 행동을 해도 되는가?"이다.
이 두 질문은 얼핏 비슷해 보이지만, 실제로는 완전히 다른 문제를 다룬다. 전자는 인증(Authentication), 후자는 인가(Authorization)에 해당한다. 실무에서는 이 두 개념이 섞여서 사용되는 경우도 많지만, 역할을 명확히 구분하지 않으면 보안 문제로 이어질 수 있다. 이번 글에서는 인증과 인가가 각각 어떤 역할을 하는지, 실제 요청 흐름에서 어떻게 동작하는지, 그리고 왜 이 둘을 반드시 분리해서 이해해야 하는지에 대해 정리해보겠다.
1. 개요
1.1. 인증(Authentication)
인증은 말 그대로 "이 사용자가 누구인지 확인하는 과정"이다. 가장 익숙한 예시는 로그인이다. 사용자가 아이디와 비밀번호를 입력하면, 서버는 해당 정보가 실제로 존재하는 사용자와 일치하는지를 확인한다. 만약 정보가 맞다면 서버는 "이 요청은 특정 사용자로부터 온 요청이다"라고 판단한다. 여기서 중요한 것은, 인증의 목적은 어디까지나 사용자의 신원을 식별하는 것이라는 점이다.
예를 들어 로그인에 성공했다는 것은 "이 사용자는 user_id=1이다"라는 사실이 확정된 상태일 뿐이다. 이 사용자가 무엇을 할 수 있는지는 아직 결정되지 않았다. 인증 단계에서는 오직 "누구인가"만이 중요하다. 이 과정은 대부분의 요청에서 반복된다. 사용자가 로그인한 이후에도, 서버는 매 요청마다 세션이나 토큰을 확인하여 해당 요청이 어떤 사용자로부터 왔는지를 식별한다. 즉, 인증은 로그인 한 번으로 끝나는 것이 아니라, 요청마다 지속적으로 수행되는 과정이다.
1.2. 인가(Authorization)
인가은 인증이 끝난 이후에 이어지는 단계이다. 이 단계에서는 "이 사용자가 이 행동을 해도 되는가?"를 판단한다.
예를 들어 어떤 사용자가 로그인에 성공했다고 해서, 그 사용자가 모든 기능을 사용할 수 있는 것은 아니다. 일반 사용자는 게시글을 작성할 수 있지만, 관리자만 게시글을 삭제할 수 있는 경우가 있다. 또는 특정 사용자는 자신의 정보만 수정할 수 있고, 다른 사용자의 정보에는 접근할 수 없어야 한다. 이처럼 인가는 사용자에게 허용된 행동의 범위를 결정하는 과정이다.
조금 더 구체적으로 보면, 인가는 보통 두 가지 요소를 기준으로 판단된다. 하나는 사용자의 역할(role)이고, 다른 하나는 요청 대상(resource)이다. 예를 들어 관리자(admin)라는 역할을 가진 사용자는 더 많은 권한을 가지게 될 수 있고, 특정 게시글의 작성자는 해당 게시글을 수정할 수 있는 권한을 가지게 된다.
즉, 인가는 단순히 "권한이 있다/없다"를 체크하는 것을 넘어, 서비스의 정책과 규칙이 그대로 반영되는 영역이다.
2. 인증 vs 인가
2.1. 개념 차이
인증과 인가는 순서와 역할이 명확하게 구분된다. 인증은 항상 먼저 수행되고, 인가는 그 다음에 수행된다. 요청이 서버에 도착하면, 가장 먼저 해야 할 일은 이 요청이 누구로부터 온 것인지 파악하는 것이다. 이를 위해 세션이나 JWT 토큰을 확인하여 사용자 정보를 추출한다. 이 단계가 인증이다.
그 다음으로는, 해당 사용자가 요청한 작업을 수행할 수 있는지 확인해야 한다. 예를 들어 게시글 삭제 요청이라면, 해당 사용자가 관리자이거나 게시글 작성자인지를 확인해야 한다. 이 단계가 인가이다. 중요한 점은, 인증이 실패하면 인가 단계까지 갈 수 없다는 것이다. 사용자가 누구인지도 모르는 상태에서는 권한을 판단할 수 없기 때문이다.
2.2. 흐름 관점
실제 요청 흐름을 조금 더 구체적으로 풀어보면 다음과 같다. 사용자가 어떤 API를 호출하면, 서버는 먼저 요청에 포함된 인증 정보를 확인한다. 세션 기반이라면 쿠키에 담긴 session_id를 확인하고, JWT 기반이라면 Authorization 헤더에 담긴 토큰을 확인한다. 이 과정을 통해 서버는 요청을 보낸 사용자의 ID를 알아낸다.
그 다음 단계에서는 해당 요청이 허용된 것인지 검증한다. 예를 들어 "게시글을 삭제한다"는 요청이라면, 이 사용자가 관리자 권한을 가지고 있는지, 혹은 해당 게시글의 작성자인지를 확인한다. 만약 조건을 만족하지 않으면 요청은 거부된다. 이 두 단계는 대부분의 웹 서비스에서 거의 동일한 구조로 반복된다. 다만 인증 방식은 세션, JWT, OAuth 등으로 다양하게 바뀔 수 있는 반면, 인가 로직은 서비스마다 전혀 다르게 구성된다.
2.3. 비유
개인적으로 인증과 인가를 이해할 때는 놀이공원을 떠올리면 가장 직관적이다. 놀이공원에 들어가는 과정을 생각해보자.
먼저 입구에서 티켓을 구매하거나 확인을 받는다. 이 과정은 "이 사람이 입장 가능한 사람인가?"를 확인하는 단계이다. 티켓이 유효하다면 입장을 허용하고, 그렇지 않다면 입장을 막는다. 여기까지가 인증과 동일한 역할을 한다. 즉, 인증은 "이 사람이 정당하게 입장한 사람인가?"를 판단하는 과정이다.
입장을 하고 나면 상황이 조금 달라진다. 놀이공원 안에 들어왔다고 해서 모든 놀이기구를 마음대로 탈 수 있는 것은 아니다. 예를 들어 어떤 놀이기구는 키 제한이 있을 수 있고, 어떤 기구는 자유이용권 티켓을 가진 사람만 이용할 수 있는 경우가 있다. 또 어떤 체험은 추가 비용을 지불해야 이용할 수 있다.
이때 직원이 "이 사람은 이 놀이기구를 이용할 수 있는 조건을 만족하는가?"를 확인하는 과정이 바로 인가이다. 즉, 인가는 "입장한 사람 중에서, 이 사람이 지금 하려는 행동이 허용되는가?"를 판단하는 단계이다. 이 비유에서 중요한 포인트는 두 가지다.
- 순서: 티켓 확인 없이 놀이기구를 탈 수는 없다. 반드시 입장(인증)이 먼저이고, 그 다음에 이용 가능 여부(인가)를 판단한다.
- 역할의 차이: 티켓은 놀이공원 전체에 대한 입장 자격을 의미한다. 반면 놀이기구 이용 여부는 각각의 시설마다 다르게 결정된다.
이걸 실제 서비스에 그대로 대응시켜 보면 다음과 같다.
- 인증 → "이 사용자가 로그인한 사용자다"
- 인가 → "이 사용자가 이 API를 호출할 수 있다"
이렇게 보면 인증은 한 번 통과하면 여러 기능에서 공통으로 사용되는 "입장권" 같은 개념이고, 인가는 각 기능마다 별도로 판단되는 "이용 조건"에 가깝다. 그래서 인증은 보통 한 번만 처리해두고 계속 재사용하는 구조로 만들고, 인가는 각 기능마다 필요한 조건을 따로 검사하는 방식으로 구현하는 것이 자연스럽다. 이렇게 이해하면 인증과 인가를 왜 분리해야 하는지도 훨씬 명확해진다.
2.4. 예시
게시글 삭제 API를 가정해보자.
사용자가 DELETE /posts/1 요청을 보냈을 때, 서버는 먼저 이 요청이 어떤 사용자로부터 왔는지를 확인한다. 이 과정에서 세션이나 JWT를 검증하고, user_id를 추출한다. 만약 인증 정보가 없거나 유효하지 않다면, 서버는 이 요청을 더 이상 처리하지 않고 바로 실패 응답을 반환한다.
인증이 완료되면, 그 다음 단계로 넘어간다. 서버는 해당 사용자가 게시글을 삭제할 수 있는지 확인한다. 이때 조건은 여러 가지가 될 수 있다.
- 해당 게시글의 작성자인지?
- 관리자 권한을 가지고 있는지?
- 특정 정책을 만족하는지?
위 조건을 만족하지 않으면 요청은 거부된다. 이 경우 서버는 "인증은 되었지만 권한이 없다"는 의미로 에러를 반환한다. 이처럼 인증과 인가는 같은 요청 안에서 이어서 수행되지만, 역할은 완전히 다르다.
3. 인증과 인가의 특징
3.1. 인증은 공통 로직이다
인증은 거의 모든 요청에서 반복되는 공통 로직이다. 사용자가 누구인지 확인하는 과정은 API의 종류와 상관없이 동일하게 적용된다. 그래서 대부분의 프레임워크에서는 인증을 미들웨어 형태로 분리해서 처리한다. 요청이 들어오면 가장 먼저 인증 로직이 실행되고, 이 단계에서 사용자 정보가 추출되어 이후 로직에서 사용할 수 있도록 전달된다. 이렇게 인증을 공통 로직으로 분리해두면, 인증 방식을 바꾸기도 쉬워진다. 예를 들어 세션 기반 인증에서 JWT 기반 인증으로 전환하더라도, 인증 미들웨어만 교체하면 나머지 로직은 그대로 유지할 수 있다.
3.2. 인가는 비즈니스 로직이다
인가는 인증과 달리 서비스의 정책이 반영되는 영역이다. 어떤 사용자가 어떤 행동을 할 수 있는지는 서비스마다 다르기 때문이다. 예를 들어 어떤 서비스에서는 게시글을 작성한 사람만 수정할 수 있도록 할 수 있고, 다른 서비스에서는 관리자만 수정할 수 있도록 할 수도 있다. 또는 특정 등급 이상의 사용자만 접근할 수 있는 기능이 있을 수도 있다. 이처럼 인가는 단순한 기술적인 처리가 아닌 "이 서비스는 어떤 규칙으로 동작하는가"를 정의하는 부분에 가깝다. 그래서 인가 로직은 단순히 role을 체크하는 수준에서 끝나지 않는 경우가 많다. 데이터의 소유 관계, 상태, 정책 등을 함께 고려해야 하는 경우가 많다.
3.3. 에러 처리의 차이
인증과 인가는 실패했을 때의 의미도 다르다. 인증이 실패했다는 것은 사용자가 누구인지 확인할 수 없다는 의미이다. 즉, 로그인하지 않았거나 인증 정보가 잘못된 상태이다. 이 경우에는 요청 자체를 처리할 수 없기 때문에 바로 거부된다. 반면 인가가 실패했다는 것은 사용자가 누구인지는 알지만, 해당 작업을 수행할 권한이 없다는 의미이다. 예를 들어 일반 사용자가 관리자 기능에 접근하려고 할 때 발생한다. 이 두 상황을 구분해서 처리하는 것은 중요하다. 그래야 클라이언트에서도 "다시 로그인해야 하는 상황인지" 아니면 "권한이 없는 상황인지"를 정확히 판단할 수 있기 때문이다.
4. 장단점 관점에서 보기
4.1. 인증과 인가를 분리했을 때의 장점
인증과 인가를 명확하게 분리하면 코드 구조가 훨씬 깔끔해진다. 인증은 모든 요청에서 공통적으로 수행되기 때문에 미들웨어로 분리하는 것이 자연스럽다. 이렇게 하면 각 API에서는 "누구인지 확인하는 코드"를 반복해서 작성할 필요가 없다. 대신 이미 인증이 완료된 상태에서 비즈니스 로직에만 집중할 수 있다. 인가 또한 별도의 로직으로 분리하면 재사용성이 높아진다. 예를 들어 "관리자만 접근 가능"이라는 조건은 여러 API에서 반복될 수 있다. 이를 공통 함수나 데코레이터로 분리하면, 동일한 정책을 일관되게 적용할 수 있다. 이렇게 역할을 나누면 유지보수도 쉬워진다. 인증 방식이 바뀌거나 권한 정책이 변경되더라도, 해당 부분만 수정하면 되기 때문이다.
4.2. 분리하지 않았을 때의 문제
반대로 인증과 인가를 명확하게 구분하지 않으면, 코드가 점점 복잡해지고 보안 문제가 발생할 수 있다. 예를 들어 어떤 API에서 user_id만 확인하고 바로 데이터를 수정하도록 구현했다고 가정해보자. 이 경우 "이 사용자가 해당 데이터를 수정할 권한이 있는지"를 체크하지 않으면, 다른 사용자의 데이터를 수정할 수 있는 문제가 발생할 수 있다. 또한 인증과 인가가 섞여 있으면 로직이 중복되기 쉽다. 어떤 API에서는 권한 체크를 하고, 다른 API에서는 권한 체크를 하지 않는 식으로 일관성이 깨질 수 있다. 이런 문제는 서비스가 커질수록 더 크게 드러난다. 결국 인증과 인가는 단순한 개념 구분이 아니라, 안전한 서비스 설계를 위한 기본 구조라고 보는 것이 맞다.
5. 사용 기준
개인적으로는 인증과 인가를 나눠서 이해할 때, "어디까지가 공통 기능이고, 어디부터가 서비스 규칙인가"라는 기준으로 보는 것이 가장 이해하기 쉽다고 생각한다. 먼저 인증은 거의 모든 요청에서 반복되는 공통 기능이다. 사용자가 로그인했는지, 이 요청이 누구로부터 온 것인지 확인하는 과정은 어떤 API를 만들든 항상 필요하다. 게시글을 조회하든, 댓글을 작성하든, 주문을 생성하든, 결국 서버는 "이 요청을 보낸 사용자가 누구인지"를 먼저 알아야 한다.
그래서 인증 로직은 보통 한 곳에 모아서 처리한다. 예를 들어 요청이 들어오면 가장 먼저 실행되는 미들웨어에서 세션이나 JWT를 확인하고, user_id를 꺼내서 요청에 붙여준다. 이렇게 하면 이후의 로직에서는 "이 사용자가 누구인지"를 다시 고민할 필요 없이 바로 사용할 수 있다. 이처럼 인증은 특정 기능에 종속되지 않고, 서비스 전반에서 공통적으로 사용되는 기반 역할을 한다. 그래서 구현할 때도 여러 곳에 흩어지게 하기보다는, 한 번만 만들어두고 모든 요청에서 재사용하는 구조로 설계하는 것이 일반적이다.
반면 인가는 성격이 완전히 다르다. 인가는 "이 사용자가 무엇을 할 수 있는가"를 결정하는 로직인데, 이건 서비스마다 다르고 기능마다도 다르다. 예를 들어 게시글 서비스에서는 "작성자만 수정 가능"이라는 규칙이 있을 수 있다. 반면 관리자 페이지에서는 "관리자만 접근 가능"이라는 규칙이 있을 수 있다. 또 어떤 서비스에서는 "VIP 사용자만 특정 기능 사용 가능" 같은 조건이 있을 수도 있다. 이처럼 인가는 단순히 기술적인 처리라기보다, 서비스가 어떤 규칙으로 동작하는지를 정의하는 부분이다. 즉, 서비스의 정책이 그대로 코드로 표현되는 영역이다.
그래서 인가는 공통으로 한 번에 처리하기보다는, 각 기능 안에서 그 상황에 맞게 검증하는 경우가 많다. 어떤 API에서는 작성자인지 확인하고, 다른 API에서는 관리자 권한을 확인하는 식이다. 정리하면 이렇게 볼 수 있다.
인증은 "이 사용자가 누구인지 확인해서 이후 로직에서 쓸 수 있게 만들어주는 공통 기능"이고,
인가는 "그 사용자가 지금 하려는 행동이 허용되는지 판단하는 서비스 규칙"이다.
이렇게 나눠서 보면 구조를 설계할 때도 훨씬 명확해진다. 인증은 최대한 한 곳에서 공통으로 처리하고, 인가는 각 기능의 요구사항에 맞게 분리해서 구현하는 것이 자연스럽다.
마치며
인증과 인가는 비슷해 보이지만, 실제로는 전혀 다른 역할을 담당한다. 인증은 사용자를 식별하는 과정이고, 인가는 그 사용자가 어떤 행동을 할 수 있는지를 결정하는 과정이다. 이 두 개념을 명확하게 구분하지 않으면 보안 문제가 발생할 수 있고, 코드 구조도 점점 복잡해질 수 있다. 반대로 두 개념을 잘 분리해서 설계하면, 구조가 명확해지고 유지보수도 쉬워진다. 결국 중요한 것은 기술적인 구현 방식보다, "사용자를 식별하고 그 사용자에게 허용된 행동만 수행하게 만든다"는 원칙을 어떻게 설계에 녹여내느냐라고 생각한다.