52장. 관리자 기능 붙이기
사용자 입장에서는 서비스가 잘 열리고, 글이 잘 써지고, 검색이 잘 되면 꽤 완성된 것처럼 보인다. 그런데 운영자 입장에서 보면 이야기가 조금 다르다. 누군가 이상한 글을 올릴 수도 있고, 신고가 들어올 수도 있고, 규칙을 반복해서 어기는 계정이 생길 수도 있다. 이때 필요한 것이 바로 관리자 기능이다.
많은 초보자는 관리자 기능을 "나중에 큰 서비스가 되면 붙이는 것"이라고 생각한다. 하지만 실제로는 그 반대에 가깝다. 서비스가 조금이라도 공개되는 순간, 운영 기능은 빠를수록 좋다. 관리자 화면이 없으면 운영자는 매번 데이터베이스를 직접 열어 보고, 코드를 수정하고, 손으로 값을 바꿔야 한다. 그것은 개발이라기보다 응급처치에 가깝다.
이번 장에서는 콘텐츠 관리, 신고 처리, 사용자 관리 같은 기본 관리자 기능이 왜 필요한지, 그리고 우리 같은 작은 풀스택 서비스에서는 어디까지 만들면 충분한지 살펴보겠다. 핵심은 단순하다.
운영자의 화면이 생기면 서비스는 한 단계 더 현실적인 제품이 된다.
![[10부] 관리자 기능 붙이기](https://images.tilnote.io/pages/e8e50b6d-cb2e-459e-bf1a-06bbf4d8065a.webp)
![[10부] 관리자 기능 붙이기](https://images.tilnote.io/pages/7d2bf53d-6224-4834-8eb9-91b67e014e25.webp)
관리자 기능은 왜 필요한가
서비스를 처음 만들 때는 보통 "사용자가 무엇을 할 수 있는가"에 집중한다. 회원가입, 로그인, 글 작성, 수정, 삭제 같은 기능이 그 예다. 그런데 서비스를 조금만 운영해 보면 곧바로 다른 질문이 생긴다.
"문제가 생기면 누가 정리하지?"
이 질문에 답하는 것이 관리자 기능이다. 사용자는 서비스를 이용하지만, 운영자는 서비스를 유지한다. 둘의 역할은 다르다.
예를 들어 보자. 누군가 욕설이 들어간 글을 올렸다고 하자. 일반 사용자 화면만 있는 서비스에서는 운영자가 할 수 있는 일이 거의 없다. 데이터베이스에 직접 들어가 삭제해야 할 수도 있고, 어느 사용자가 올렸는지 찾는 일도 번거롭다. 반면 관리자 화면이 있다면 목록에서 바로 확인하고, 숨기거나 삭제하고, 필요하면 계정 상태까지 점검할 수 있다.
서비스는 만드는 순간보다 운영하는 순간에 진짜 실력이 드러난다. 관리자 기능은 바로 그 운영의 시작점이다.
일반 권한과 관리자 권한은 다르다
앞에서 우리는 "내 글과 남의 글을 구분하는 권한"을 배웠다. 그것은 사용자끼리의 경계를 세우는 작업이었다. 관리자 기능은 그보다 한 단계 위의 이야기다. 여기서는 특정 사용자가 운영을 위해 더 넓은 권한을 가진다.
쉽게 말해 이렇게 구분하면 된다.
일반 사용자는 자기 데이터를 다룬다. 관리자는 서비스 전체를 다룬다.
이 차이는 아주 중요하다. 일반 사용자는 자기 글만 수정할 수 있어야 하지만, 관리자는 신고된 글을 숨기거나 삭제할 수 있어야 한다. 일반 사용자는 다른 사람 계정을 볼 이유가 없지만, 관리자는 문제 계정을 확인해야 할 수 있다.
그렇다고 해서 관리자에게 모든 권한을 무작정 주면 안 된다. "관리자니까 다 가능"은 편해 보이지만 위험하다. 초보 단계에서는 오히려 더 단순하게 설계하는 편이 좋다. 예를 들어 우리 서비스에서는 다음 정도면 충분하다.
관리자는 신고 목록을 볼 수 있다.
관리자는 글을 숨기거나 삭제할 수 있다.
관리자는 사용자 목록과 기본 상태를 확인할 수 있다.
관리자는 필요하면 특정 사용자를 제한 상태로 바꿀 수 있다.
이 정도만 있어도 운영 감각이 확 달라진다. 중요한 것은 거대한 관리자 시스템을 한 번에 만드는 것이 아니라, 운영에 꼭 필요한 최소한의 도구를 먼저 갖추는 일이다.
어떤 관리자 기능부터 만들어야 할까
관리자 기능은 생각보다 종류가 많다. 그래서 처음에는 욕심이 생긴다. 대시보드, 통계, 권한 단계, 공지 발송, 배너 관리까지 다 만들고 싶어진다. 하지만 초보자에게 필요한 출발점은 훨씬 작다.
가장 먼저 붙이기 좋은 것은 세 가지다.
콘텐츠 관리: 운영자는 정리하는 사람이다
첫 번째는 콘텐츠 관리다. 우리 서비스가 게시글 중심이라면 운영자가 글을 보고 상태를 바꿀 수 있어야 한다.
예를 들어 이런 기능이 있으면 좋다.
전체 글 목록 보기
공개 중인 글과 숨김 처리된 글 구분하기
신고가 많이 들어온 글 빠르게 찾기
글을 숨기거나 삭제하기
여기서 중요한 포인트는 "삭제"만 생각하지 않는 것이다. 실제 운영에서는 바로 지우는 것보다 숨김 처리가 더 유용한 경우가 많다. 왜냐하면 잘못 삭제했을 때 되돌리기 어렵기 때문이다.
그래서 초보자라면 deleted 같은 강한 상태 하나만 두기보다, visible, hidden, deleted처럼 상태값을 나누는 방법을 먼저 떠올려 보면 좋다. 그러면 운영이 훨씬 부드러워진다.
예를 들어 글 테이블에 status 필드를 두면 이런 식으로 다룰 수 있다.
status: 'visible' | 'hidden' | 'deleted'사용자에게는 visible인 글만 보여 주고, 관리자 화면에서는 모든 상태를 보게 만들 수 있다. 이 구조는 단순하지만 꽤 실전적이다.
신고 처리: 사용자 목소리를 운영 흐름으로 바꾸기
두 번째는 신고 처리다. 신고 기능이 없으면 운영자는 문제를 발견하기 어렵고, 사용자는 불편을 표현할 통로가 없다.
신고 기능은 거창할 필요가 없다. 아주 작은 형태로 시작해도 된다. 예를 들어 글 상세 화면에 "신고하기" 버튼을 두고, 이유를 간단히 선택하게 만들면 된다.
신고 데이터는 보통 이런 정보를 담는다.
누가 신고했는가
무엇을 신고했는가
왜 신고했는가
현재 처리 상태는 무엇인가
이때 처리 상태가 중요하다. 신고는 한 번 들어오고 끝나는 데이터가 아니라, 운영자의 판단을 거치는 업무 흐름이기 때문이다. 그래서 pending, reviewed, resolved, rejected 같은 상태를 두면 좋다.
예를 들어 신고 테이블을 아주 단순하게 만들면 이런 느낌이다.
{
id: string
post_id: string
reporter_user_id: string
reason: string
status: 'pending' | 'reviewed' | 'resolved' | 'rejected'
created_at: string
}이렇게만 해도 운영자는 "아직 안 본 신고"와 "이미 처리한 신고"를 나눠서 볼 수 있다. 서비스가 갑자기 어른스러워지는 순간이다.
사용자 관리: 계정도 운영 대상이다
세 번째는 사용자 관리다. 초보자는 흔히 관리자 기능을 콘텐츠 관리로만 생각한다. 하지만 실제로는 사용자도 중요한 운영 대상이다.
운영자가 사용자 목록을 볼 수 있으면 여러 상황에 대응하기 쉬워진다. 예를 들어 신고가 반복되는 계정을 찾거나, 가입만 하고 활동이 없는 계정을 살펴보거나, 특정 사용자의 상태를 제한할 수 있다.
사용자 관리에서 처음부터 복잡한 권한 체계를 만들 필요는 없다. 대신 아주 단순한 상태값으로 시작하면 된다.
active: 정상 이용 중suspended: 일시 제한banned: 영구 제한
이 상태만 있어도 운영자는 문제 상황에 훨씬 유연하게 대응할 수 있다. 예를 들어 suspended 상태의 사용자는 로그인은 되지만 글 작성은 막을 수도 있고, 아예 로그인을 차단할 수도 있다. 중요한 것은 기능보다 규칙이다.
먼저 "제한 상태의 사용자는 무엇을 못 하게 할 것인가?"를 정하고, 그다음 코드로 옮기면 된다.
관리자 화면은 어떻게 생겨야 할까
관리자 화면이라고 해서 화려할 필요는 없다. 오히려 초보 단계에서는 너무 많은 정보를 한꺼번에 보여 주면 더 헷갈린다. 첫 버전은 단순한 구조가 좋다.
예를 들면 /admin 아래에 이렇게 나누면 충분하다.
/admin/posts: 글 관리/admin/reports: 신고 관리/admin/users: 사용자 관리
첫 화면인 /admin은 운영용 홈처럼 쓰면 된다. 오늘 들어온 신고 수, 숨김 처리된 글 수, 최근 가입자 수 같은 간단한 숫자만 보여 줘도 훌륭하다. 꼭 멋진 대시보드 차트가 없어도 괜찮다. 운영자는 보통 "무슨 일이 생겼는지 빨리 파악하는 것"을 원한다.
관리자 화면에서는 일반 사용자 UI보다 명확함이 더 중요하다. 버튼 이름도 분명해야 한다. "확인"보다는 "숨김 처리", "삭제", "정지", "복구"처럼 결과가 바로 보이는 표현이 낫다. 운영 기능은 한 번 잘못 누르면 영향이 크기 때문이다.
관리자만 들어갈 수 있게 막아야 한다
여기서 가장 중요한 것은 관리자 화면을 예쁘게 만드는 일이 아니라, 관리자가 아닌 사용자는 들어올 수 없게 막는 것이다.
초보자가 자주 하는 실수는 메뉴만 숨기는 것이다. 화면에서 관리자 링크를 안 보이게 하면 끝났다고 느끼기 쉽다. 하지만 주소를 직접 입력하면 들어갈 수 있다면 아무 의미가 없다.
관리자 권한 검사는 반드시 서버 쪽에서 해야 한다. 예를 들어 로그인한 사용자의 role 값을 확인해서 admin인 경우에만 /admin 페이지를 열어 주는 식이다.
구조는 대체로 이런 흐름이다.
사용자가 로그인한다.
서버에서 현재 사용자를 확인한다.
사용자 정보에서
role또는is_admin값을 읽는다.관리자가 아니면 메인 화면이나 오류 페이지로 돌려보낸다.
코드 느낌으로 보면 이런 식이다.
import { redirect } from 'next/navigation'
import { createClient } from '@/lib/supabase/server'
export default async function AdminPage() {
const supabase = await createClient()
const {
data: { user },
} = await supabase.auth.getUser()
if (!user) {
redirect('/login')
}
const { data: profile } = await supabase
.from('profiles')
.select('role')
.eq('id', user.id)
.single()
if (!profile || profile.role !== 'admin') {
redirect('/')
}
return <main>관리자 화면</main>
}핵심은 이것이다. 관리자 여부는 브라우저가 아니라 서버가 판단해야 한다. 그래야 사용자가 화면을 억지로 조작해도 권한을 속이기 어렵다.
role은 어디에 저장할까
관리자 여부를 저장하는 방법은 여러 가지가 있지만, 입문 단계에서는 가장 단순한 방식이 좋다. 보통은 사용자 프로필 테이블에 role 컬럼을 둔다.
예를 들어 profiles 테이블에 이런 구조를 둘 수 있다.
id uuid primary key,
email text,
role text default 'user',
status text default 'active'그러면 대부분의 사용자는 role = 'user'이고, 운영 계정만 role = 'admin'이 된다.
이 방식의 장점은 이해하기 쉽다는 것이다. 게다가 나중에 필요하면 manager, editor 같은 역할을 더 추가할 수도 있다. 하지만 처음부터 너무 세분화하지는 말자. user와 admin만 있어도 대부분의 첫 서비스에는 충분하다.
관리자 기능도 작은 단위로 나누어 만들자
관리자 기능은 범위가 넓어서 AI에게 한 번에 맡기면 종종 산으로 간다. "어드민 시스템 만들어 줘"라고 하면 지나치게 큰 구조, 현재 프로젝트와 맞지 않는 라이브러리, 불필요한 대시보드까지 한꺼번에 나올 수 있다.
이럴 때도 잘게 나누는 원칙이 통한다.
예를 들어 이런 순서가 좋다.
먼저
profiles테이블에role과status를 추가한다.그다음
/admin경로를 만들고 관리자만 접근 가능하게 한다.그다음
/admin/posts에서 전체 글 목록을 보여 준다.이후 숨김 처리 버튼을 붙인다.
그다음
/admin/reports를 만들고 신고 상태 변경 기능을 넣는다.마지막으로
/admin/users에서 사용자 상태를 바꾸게 한다.
이 순서가 좋은 이유는 매 단계가 독립적으로 확인 가능하기 때문이다. 오늘은 관리자 진입까지만, 내일은 글 관리까지만, 이런 식으로 나눠도 된다. 운영 기능은 한 방에 완성하는 것이 아니라, 실제 문제를 해결하면서 자란다.
삭제보다 상태 변경이 더 안전할 때가 많다
운영 기능에서 꼭 기억할 원칙이 하나 있다. 바로 지우는 것보다 상태를 바꾸는 편이 안전하다.
왜 그럴까. 운영자는 바쁜 상태에서 판단할 때가 많고, 때로는 잘못 누를 수도 있다. 게시글을 진짜 삭제해 버리면 복구가 어렵다. 하지만 hidden 상태로 바꾸는 것이라면 되돌리기 쉽다.
사용자 관리도 마찬가지다. 계정을 곧바로 삭제하는 대신 suspended로 바꾸면 일단 문제를 멈추고 나중에 다시 검토할 수 있다. 신고도 바로 "완료"로 끝내기보다 reviewed 같은 중간 상태를 두면 흐름이 더 명확해진다.
초보 단계에서 이 감각을 익혀 두면 이후 어떤 서비스로 가도 큰 도움이 된다. 제품은 종종 코드보다 상태 설계에서 더 안정해진다.
운영 기록도 남기면 훨씬 좋다
관리자 기능을 조금 더 현실적으로 만들고 싶다면, 운영 기록을 남기는 습관을 들여 보자. 예를 들어 누가 어떤 글을 숨겼는지, 어떤 신고를 처리했는지 기록하면 나중에 상황을 추적하기 쉽다.
처음부터 완벽한 감사 로그 시스템을 만들 필요는 없다. 아주 작은 테이블 하나만 있어도 된다.
{
id: string
admin_user_id: string
action: string
target_type: string
target_id: string
note: string | null
created_at: string
}예를 들어 "관리자 A가 게시글 123을 hidden으로 바꿨다" 같은 정보가 남으면, 나중에 왜 이런 상태가 되었는지 이해하기가 쉬워진다. 운영 기록은 서비스가 커질수록 더 중요해진다. 작은 서비스에서도 미리 감을 잡아 두면 좋다.
우리 서비스에 붙인다면 어떤 모습일까
이 책의 예제 서비스는 글을 쓰고 읽고 검색하는 작은 풀스택 웹 서비스다. 여기에 관리자 기능을 붙인다면 가장 자연스러운 그림은 이렇다.
관리자는 /admin에서 최근 신고 수와 숨김 글 수를 본다. /admin/posts에서는 전체 글 목록을 보고, 문제가 있는 글을 숨김 처리하거나 삭제할 수 있다. /admin/reports에서는 신고 내용을 확인하고 처리 상태를 바꾼다. /admin/users에서는 사용자 목록을 보고, 필요하면 특정 계정을 제한 상태로 바꾼다.
여기까지 오면 서비스는 더 이상 "기능 데모"가 아니다. 사용자 행동이 쌓이고, 문제가 생기고, 그것을 운영자가 다루는 작은 제품이 된다.
이 차이는 꽤 크다. 사용자 기능만 있는 서비스는 아직 무대 위에 올라가기 전의 리허설 같고, 관리자 기능이 붙은 서비스는 관객이 들어온 뒤의 공연장에 가깝다.
AI에게 이렇게 요청해 보자
관리자 기능은 AI에게 맡기기 좋은 주제지만, 역시 조건을 분명히 말해야 한다. 특히 권한, 경로, 데이터 구조를 정확히 적는 것이 중요하다.
이런 식으로 요청해 볼 수 있다.
현재 Next.js + Supabase 프로젝트에서 profiles 테이블의 role 값이 admin인 사용자만 /admin 경로에 접근할 수 있도록 서버 컴포넌트 코드를 작성해줘.posts 테이블에 status 컬럼을 추가하고, /admin/posts에서 전체 글 목록과 숨김 처리 버튼을 구현하는 방향으로 코드를 제안해줘.reports 테이블 스키마를 단순하게 설계해줘. 신고 대상은 게시글이고, 상태는 pending, reviewed, resolved, rejected 네 가지로 관리하고 싶어.관리자 페이지에서 사용자 목록을 보여 주고 status를 active, suspended, banned로 바꾸는 서버 액션 예시를 만들어줘.
이때 "우리 프로젝트는 입문용이고, 복잡한 라이브러리 없이 App Router와 Supabase 중심으로 가고 싶다" 같은 조건을 붙이면 결과가 훨씬 실용적이 된다.
자주 하는 실수
관리자 기능을 처음 붙일 때는 비슷한 함정에 자주 빠진다.
관리자 메뉴만 숨기고 실제 경로 보호는 하지 않는다.
role값은 만들었지만 서버에서 검사하지 않는다.운영 기능을 전부 삭제 중심으로 설계해서 되돌리기 어렵다.
신고 기능은 만들었지만 처리 상태가 없어 운영 흐름이 꼬인다.
사용자 관리가 필요해졌는데도 데이터베이스를 직접 열어 손으로 바꾼다.
이 중 가장 위험한 것은 첫 번째다. 화면에서 안 보인다고 안전한 것이 아니다. 관리자 기능은 특히 더 조심해야 한다. 운영 화면은 일반 기능보다 영향이 크기 때문이다.
정리
이번 장에서는 관리자 기능이 왜 필요한지, 그리고 초보자가 어떤 순서로 붙이면 좋은지 살펴봤다.
핵심은 세 가지다.
첫째, 관리자 기능은 거대한 회사만의 도구가 아니다. 작은 서비스라도 공개되는 순간 운영 기능이 필요해진다.
둘째, 처음부터 모든 것을 만들 필요는 없다. 콘텐츠 관리, 신고 처리, 사용자 관리라는 세 축만 있어도 서비스는 훨씬 현실적인 제품이 된다.
셋째, 관리자 기능의 본질은 화려한 대시보드가 아니라 운영자가 문제를 빠르게 보고, 판단하고, 조치할 수 있게 만드는 것이다.
이제 여러분의 서비스에는 사용자만 있는 것이 아니다. 뒤에서 질서를 잡는 운영자도 생긴다. 그리고 바로 그 순간, 서비스는 한층 더 진짜에 가까워진다.
다음 장에서는 여기서 한 걸음 더 나아가, 많은 초보자가 가장 궁금해하는 주제 가운데 하나인 결제와 구독 기능을 가볍게 맛보겠다. 서비스를 운영하다 보면 결국 이런 질문과 만나게 된다.
"이 서비스를 돈 버는 구조로 연결하려면 어떻게 해야 할까?"
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.