불필요한 조회 없이 권한 검증을 설계하는 법
로그인 사용자를 한 번만 확인하고 접근 검증의 책임을 좁히며, 시스템 관리자와 채널 단위 권한을 구분하는 설계.
출처 및 AI 안내: 이 글은 제미니의 개발실무 유튜브를 기반으로 작성되었습니다.
gpt-5.6-sol모델을 사용해 생성·편집했습니다.
권한 코드에서 로그인 사용자를 확인하는 일, 자원을 읽는 일, 그 사용자가 접근할 수 있는지 판단하는 일이 한데 섞이면 흐름을 이해하기 어려워진다. 바깥에서 이미 끝낸 작업을 데이터베이스 조회로 반복하게 되고, 서로 다른 책임을 가리키는 권한 개념도 하나의 이름 뒤에 숨기 쉽다.
간단한 채널 정책을 생각해 보자. 공개 채널은 누구나 접근할 수 있다. 비공개 채널은 시스템 관리자나 구독자만 접근할 수 있고, 나머지는 403 응답을 받는다. 정책은 짧지만 각 판단을 어디에서 수행하느냐에 따라 조회 비용과 테스트의 모양이 달라진다.
같은 사용자를 두 번 확인하지 않는다
서비스가 로그인 사용자 식별자를 받은 뒤 유저 리더를 호출해 그 사용자를 다시 조회한다고 해 보자. 이 조회를 그대로 두기 전에 넘어온 값이 무엇을 뜻하는지부터 확인해야 한다. 바깥 계층에서 요청을 이미 인증했고 유효한 로그인 사용자로 해석했다면, 안쪽 흐름에는 필요한 권한까지 담긴 그 객체를 넘기는 편이 자연스럽다. 다시 조회해도 새로 알게 되는 것이 없다면 일만 늘어난다.
나라면 사용자를 가장 바깥 경계에서 확인해 안으로 넘긴다. 스프링을 쓰는지, 계층이 몇 단계인지와는 무관하다. 핵심은 접근 흐름이 시작되기 전에 인증과 사용자 확인을 한 번만 끝내는 것이다.
다만 이 예제에는 조건이 하나 있다. 공개 채널이라면 사용자 자체가 필요하지 않을 수 있다. 채널을 먼저 읽고 비공개일 때만 사용자를 확인하는 흐름도 가능하다. 반면 많은 애플리케이션에서는 로그인 사용자를 요청마다 한 번 조회하거나 캐시한다. 어느 쪽이 맞는지는 그 요청 경로에 달려 있다. 좁게 잡아 지킬 원칙은 호출자가 이미 신뢰할 수 있는 사용자 객체를 넘겼다면 안쪽에서 같은 사용자를 다시 조회하지 않는다는 것이다.
밸리데이터는 결과를 보고하지 않고 판단한다
질문에 제시된 두 방식 가운데서는 채널을 읽은 뒤 접근 판단을 전담하는 밸리데이터에 넘기는 쪽으로 먼저 시작하겠다. 흐름을 정리하면 다음과 같다.
- 채널을 읽는다.
- 접근 밸리데이터에 채널과 확인된 사용자를 넘겨 검증한다.
- 검증이 끝나면 채널을 반환한다.
공개 여부와 관리자 권한만으로 결정할 수 없을 때 밸리데이터가 구독 정보를 조회하면 된다. 호출자가 다시 해석할 불리언을 반환할 필요는 없다. 접근할 수 없다면 403으로 이어질 예외를 던지고, 정상적으로 돌아오면 서비스가 다음 작업을 계속한다.
이 구조는 검증에 데이터가 전혀 필요하지 않은 척하지 않으면서도 자원 조회와 권한 판단을 나눈다. 구독 조회는 권한 판단을 뒷받침하는 밸리데이터 뒤에 남는다. 채널을 읽는 동작과 접근 정책을 따로 테스트할 수 있고, 밸리데이터 테스트에서는 그 경계의 구독 리더만 모킹하면 된다.
권한을 포함한 리더도 가능한 구조다
별도의 밸리데이터만이 답은 아니다. 여러 호출자가 접근 가능한 채널만 반복해서 읽는다면 그 행위를 리더의 명시적인 기능으로 만들 수도 있다. 리포지토리에서 채널을 읽고, 공개 여부를 확인한 다음, 필요한 관리자 권한과 구독 여부를 검사해 어느 조건이든 통과할 때만 채널을 반환하는 식이다.
전체 동작을 채널 리더에 두면 호출부가 단순해지고 검증을 빼먹을 가능성도 줄어든다. 어느 구조가 더 나은지는 이미 나뉜 계층과 리더의 재사용 범위에 달려 있다. 전체 코드를 보지 않은 상태에서 둘 중 하나를 보편적인 규칙으로 만들 수는 없다. 경계가 실제 행위를 표현하는지, 그 안의 데이터 접근에 분명한 목적이 있는지를 보는 편이 중요하다.
관리자가 무엇을 소유하는지 분명히 한다
더 큰 문제는 admin이라는 말에 숨어 있을 수 있다. 이 사용자는 특정 채널의 관리자인가, 아니면 모든 채널의 경계를 넘을 수 있는 시스템 전체 관리자인가? 두 역할은 같은 비공개 채널을 열 수 있더라도 서로 다른 비즈니스 개념이다.
시스템 전체 관리자 권한을 일반 사용자 모델에 바로 결합하면 사용자를 다루는 흐름마다 isAdmin 분기가 따라붙을 수 있다. 보통의 채널 경계를 적용받지 않는 권한이라면 별도의 관리자 경로나 역할로 나눌 이유가 있다. 반면 특정 채널의 관리자인지를 확인하는 일은 채널 단위 접근 판단에 자연스럽게 속하고, 어느 채널인지도 함께 알아야 한다.
하나의 객체가 시스템 관리자, 채널 관리자, 구독자를 동시에 뜻할 수 있다면 모델을 다시 살펴볼 필요가 더 커진다. 역할이 겹치는 것 자체는 가능하지만 의도한 결과여야 한다. 개념이 정리되지 않은 상태에서는 코드를 서비스에서 밸리데이터로, 다시 리더로 옮겨도 구현이 깔끔해지지 않는다. 모호함의 위치만 달라질 뿐이다.
가장 작은 책임 분리부터 시작한다
설명된 코드만 놓고 보면 첫 구현에서는 로그인 사용자를 바깥에서 확인하고, 채널을 한 번 읽은 뒤, 남은 접근 판단만 수행하고 거부 시 예외를 던지는 밸리데이터를 호출하겠다. 작고 테스트하기 쉬운 분리이며 불필요한 사용자 조회도 피할 수 있다.
그다음에는 클래스를 다듬기 전에 관리자의 의미부터 확인해야 한다. 시스템 전체 권한이라면 채널 소유권과 분리한다. 권한이 적용된 채널 조회가 애플리케이션 곳곳에서 반복된다면 전체 행위를 리더의 기능으로 올리는 방안도 검토한다. 최종 구조는 맥락에 따라 달라지지만, 중복 조회와 흐릿한 권한 소유권은 현재 책임을 다시 나눠 볼 만하다는 신호다.