LabHub

블로그

Zero Trust와 Identity-Aware Proxy — BeyondCorp 모델 직접 구축하기

한국어English日本語

들어가며 — 왜 지금 Identity-Aware Proxy인가

2026년 현재, "사내망에 있으면 신뢰한다"는 가정은 사실상 폐기된 보안 모델입니다. 원격 근무가 표준이 되었고, 워크로드는 멀티 클라우드에 흩어져 있으며, SaaS와 사내 도구의 경계도 흐려졌습니다. 여기에 AI agent가 사람을 대신해 내부 시스템에 접근하는 흐름까지 더해지면서, "누가(또는 무엇이) 어떤 컨텍스트에서 접근하는가"를 매 요청마다 검증하는 identity-first 보안이 기본값이 되었습니다.

이 글에서는 Zero Trust의 핵심 구현체인 Identity-Aware Proxy(IAP)를 다룹니다. Google의 BeyondCorp 논문이 제시한 모델을 이해하고, oauth2-proxy와 Keycloak을 조합해 IAP를 직접 구축하는 전 과정을 YAML과 설정 파일 수준까지 따라가 봅니다. 마지막에는 Cloudflare Access, Google Cloud IAP, Pomerium 같은 상용/오픈소스 대안을 비교하고, 단계적 도입 로드맵과 AI agent 시대의 접근 제어까지 살펴봅니다.

경계 보안의 종말과 identity-first 보안

성곽과 해자 모델의 한계

전통적 네트워크 보안은 흔히 "성곽과 해자(castle-and-moat)" 모델로 불립니다. 방화벽과 VPN으로 경계를 치고, 일단 내부로 들어온 트래픽은 폭넓게 신뢰하는 방식입니다. 이 모델의 문제는 명확합니다.

Zero Trust의 핵심 원칙

Zero Trust는 NIST SP 800-207에서 표준화된 개념으로, 다음 원칙으로 요약됩니다.

  1. 네트워크 위치는 신뢰의 근거가 아니다 — 사내망이든 카페 와이파이든 동일하게 취급합니다.
  2. 모든 접근은 인증되고 인가되어야 한다 — 세션 단위가 아니라 요청 단위로 검증합니다.
  3. 최소 권한 — 사용자와 디바이스는 업무에 필요한 리소스에만 접근합니다.
  4. 컨텍스트 기반 동적 정책 — 사용자 신원, 디바이스 상태, 위치, 시간 등을 종합해 접근을 결정합니다.
  5. 지속적 검증 — 한 번 통과했다고 끝이 아니라, 신호가 바뀌면 접근도 재평가됩니다.

이 원칙을 HTTP 애플리케이션 계층에서 구현하는 컴포넌트가 바로 Identity-Aware Proxy입니다.

BeyondCorp 논문의 핵심

Google은 2009년 Operation Aurora 침해 사고를 계기로 사내 VPN을 단계적으로 제거하고, 모든 사내 애플리케이션을 인터넷에 노출하되 접근 프록시로 보호하는 BeyondCorp 프로젝트를 시작했습니다. BeyondCorp 논문 시리즈의 핵심 구성요소는 다음과 같습니다.

BeyondCorp가 남긴 가장 중요한 교훈은 두 가지입니다. 첫째, 인증과 인가를 애플리케이션에서 떼어내 프록시 계층으로 중앙화하면 수백 개 사내 앱의 보안 수준을 일괄 상향할 수 있습니다. 둘째, 사용자 신원만으로는 부족하며 디바이스 신뢰가 함께 평가되어야 합니다.

IAP 동작 원리 — 모든 요청에 인증과 인가

IAP의 동작을 한 장으로 표현하면 다음과 같습니다.

                        +----------------------------+
                        |   Identity Provider (IdP)  |
                        |   Keycloak / OIDC          |
                        +-------------+--------------+
                                      | (2) OIDC 인증
                                      | Authorization Code + PKCE
+----------+   (1) HTTPS   +----------+-----------+   (4) 헤더 주입   +-------------+
|  사용자   +-------------->+  Identity-Aware Proxy +----------------->+  내부 앱     |
|  브라우저 |               |  (oauth2-proxy 등)    |  X-Forwarded-*   |  (admin 등)  |
+----------+               +----------+-----------+                  +-------------+
                                      |
                                      | (3) 정책 평가
                                      |  - 사용자 그룹/역할
                                      |  - 디바이스 상태
                                      |  - 위치/시간 컨텍스트
                                      v
                           +----------------------+
                           |  Policy / Device DB  |
                           +----------------------+

요청 처리 흐름을 단계별로 풀면 다음과 같습니다.

  1. 사용자가 보호 대상 앱의 URL에 접근하면, 요청은 반드시 IAP를 거칩니다. 앱은 IAP를 우회한 직접 접근을 네트워크 정책으로 차단합니다.
  2. 유효한 세션이 없으면 IAP가 OIDC Authorization Code Flow(PKCE 포함)로 IdP에 인증을 위임합니다.
  3. 인증이 끝나면 IAP는 ID 토큰의 클레임(그룹, 역할)과 추가 컨텍스트(디바이스, IP, 시간)를 바탕으로 인가 정책을 평가합니다.
  4. 통과한 요청에만 사용자 신원 헤더(또는 서명된 JWT)를 붙여 업스트림 앱으로 전달합니다.
  5. 이 과정은 최초 로그인 이후에도 모든 요청마다 세션 쿠키 검증과 정책 재평가 형태로 반복됩니다. 세션은 짧게 유지하고, refresh 시점마다 IdP의 최신 상태(계정 비활성화, 그룹 변경)를 반영합니다.

전통적 VPN과의 차이를 표로 정리하면 다음과 같습니다.

항목VPNIdentity-Aware Proxy
신뢰 단위네트워크 (들어오면 끝)요청 (매 요청 인증/인가)
접근 범위사내망 전체 또는 서브넷애플리케이션 단위
인가 기준IP, 계정신원 + 그룹 + 디바이스 + 컨텍스트
가시성터널 내부는 불투명요청 단위 감사 로그
사용자 경험클라이언트 설치, 접속 단절브라우저만으로 SSO
L3/L4 지원가능기본은 HTTP, TCP는 별도 솔루션

실전 구축 — oauth2-proxy + Keycloak으로 IAP 만들기

이제 직접 만들어 보겠습니다. 구성은 다음과 같습니다.

1단계 — Keycloak 클라이언트 설정

먼저 Keycloak에 oauth2-proxy용 confidential client를 만듭니다. realm은 internal이라고 가정합니다. Keycloak Admin CLI(kcadm)로 다음과 같이 생성합니다.

# Keycloak admin CLI 로그인
kcadm.sh config credentials \
  --server https://keycloak.example.com \
  --realm master \
  --user admin

# oauth2-proxy용 클라이언트 생성
kcadm.sh create clients -r internal -f - <<'EOF'
{
  "clientId": "oauth2-proxy",
  "protocol": "openid-connect",
  "publicClient": false,
  "standardFlowEnabled": true,
  "directAccessGrantsEnabled": false,
  "serviceAccountsEnabled": false,
  "redirectUris": ["https://admin.example.com/oauth2/callback"],
  "attributes": {
    "pkce.code.challenge.method": "S256",
    "post.logout.redirect.uris": "https://admin.example.com"
  }
}
EOF

그룹 정보를 토큰에 담기 위해 groups 클레임 매퍼를 추가합니다.

# groups 클레임을 ID 토큰에 포함시키는 매퍼
CLIENT_UUID=$(kcadm.sh get clients -r internal -q clientId=oauth2-proxy --fields id --format csv --noquotes)

kcadm.sh create clients/$CLIENT_UUID/protocol-mappers/models -r internal -f - <<'EOF'
{
  "name": "groups",
  "protocol": "openid-connect",
  "protocolMapper": "oidc-group-membership-mapper",
  "config": {
    "claim.name": "groups",
    "full.path": "false",
    "id.token.claim": "true",
    "access.token.claim": "true",
    "userinfo.token.claim": "true"
  }
}
EOF

Keycloak 26.6부터는 로그인 폼에 passkeys가 조건부 UI로 통합되어 있으므로, internal realm의 인증 플로우에서 WebAuthn Passwordless 정책을 활성화하면 비밀번호 없는 1차 인증을 기본값으로 만들 수 있습니다. Zero Trust에서 피싱 저항성 있는 인증 수단은 사실상 필수입니다 (WebAuthn Level 3, FIDO passkeys 참고).

2단계 — oauth2-proxy 배포

쿠버네티스에 oauth2-proxy를 배포합니다. 전체 매니페스트는 다음과 같습니다.

apiVersion: v1
kind: Secret
metadata:
  name: oauth2-proxy-secrets
  namespace: iap
type: Opaque
stringData:
  client-secret: 'REPLACE_WITH_KEYCLOAK_CLIENT_SECRET'
  # python -c 'import secrets; print(secrets.token_urlsafe(32))'
  cookie-secret: 'REPLACE_WITH_32_BYTE_RANDOM'
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: oauth2-proxy
  namespace: iap
  labels:
    app: oauth2-proxy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: oauth2-proxy
  template:
    metadata:
      labels:
        app: oauth2-proxy
    spec:
      containers:
        - name: oauth2-proxy
          image: quay.io/oauth2-proxy/oauth2-proxy:v7.8.1
          args:
            - --provider=keycloak-oidc
            - --client-id=oauth2-proxy
            - --oidc-issuer-url=https://keycloak.example.com/realms/internal
            - --code-challenge-method=S256
            - --redirect-url=https://admin.example.com/oauth2/callback
            - --email-domain=example.com
            - --allowed-group=platform-admins
            - --scope=openid email profile groups
            - --cookie-secure=true
            - --cookie-samesite=lax
            - --cookie-refresh=4m
            - --cookie-expire=8h
            - --session-store-type=redis
            - --redis-connection-url=redis://redis.iap.svc.cluster.local:6379
            - --set-xauthrequest=true
            - --set-authorization-header=true
            - --pass-access-token=false
            - --skip-provider-button=true
            - --reverse-proxy=true
            - --http-address=0.0.0.0:4180
            - --metrics-address=0.0.0.0:44180
          env:
            - name: OAUTH2_PROXY_CLIENT_SECRET
              valueFrom:
                secretKeyRef:
                  name: oauth2-proxy-secrets
                  key: client-secret
            - name: OAUTH2_PROXY_COOKIE_SECRET
              valueFrom:
                secretKeyRef:
                  name: oauth2-proxy-secrets
                  key: cookie-secret
          ports:
            - containerPort: 4180
              name: http
            - containerPort: 44180
              name: metrics
          readinessProbe:
            httpGet:
              path: /ping
              port: 4180
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
  name: oauth2-proxy
  namespace: iap
spec:
  selector:
    app: oauth2-proxy
  ports:
    - name: http
      port: 4180
      targetPort: 4180

설정에서 눈여겨볼 부분은 다음과 같습니다.

3단계 — nginx auth_request 패턴 연동

nginx의 auth_request는 "본 요청을 처리하기 전에 서브요청을 보내 인증 여부를 확인"하는 지시어입니다. IAP 패턴의 핵심 접착제입니다.

server {
    listen 443 ssl;
    server_name admin.example.com;

    ssl_certificate     /etc/nginx/tls/tls.crt;
    ssl_certificate_key /etc/nginx/tls/tls.key;

    # 인증 검사 서브요청 — oauth2-proxy의 /oauth2/auth 는
    # 인증되어 있으면 202, 아니면 401을 반환한다
    location = /oauth2/auth {
        internal;
        proxy_pass http://oauth2-proxy.iap.svc.cluster.local:4180;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        # 바디는 전달할 필요 없음
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
    }

    # 로그인 플로우 (리다이렉트, 콜백, 로그아웃)
    location /oauth2/ {
        proxy_pass http://oauth2-proxy.iap.svc.cluster.local:4180;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location / {
        auth_request /oauth2/auth;
        # 401이면 로그인으로 리다이렉트
        error_page 401 = /oauth2/start?rd=$scheme://$host$request_uri;

        # auth 서브요청 응답 헤더에서 신원 정보를 꺼내 업스트림에 주입
        auth_request_set $user   $upstream_http_x_auth_request_user;
        auth_request_set $email  $upstream_http_x_auth_request_email;
        auth_request_set $groups $upstream_http_x_auth_request_groups;
        proxy_set_header X-Forwarded-User   $user;
        proxy_set_header X-Forwarded-Email  $email;
        proxy_set_header X-Forwarded-Groups $groups;

        # 사용자가 위조한 신원 헤더가 통과하지 못하도록
        # 위 proxy_set_header가 항상 덮어쓰는 구조를 유지한다

        proxy_pass http://admin-dashboard.apps.svc.cluster.local:8080;
    }
}

쿠버네티스 ingress-nginx를 쓴다면 같은 패턴을 어노테이션으로 표현할 수 있습니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: admin-dashboard
  namespace: apps
  annotations:
    nginx.ingress.kubernetes.io/auth-url: 'https://admin.example.com/oauth2/auth'
    nginx.ingress.kubernetes.io/auth-signin: 'https://admin.example.com/oauth2/start?rd=$scheme://$host$request_uri'
    nginx.ingress.kubernetes.io/auth-response-headers: 'X-Auth-Request-User, X-Auth-Request-Email, X-Auth-Request-Groups'
spec:
  ingressClassName: nginx
  rules:
    - host: admin.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: admin-dashboard
                port:
                  number: 8080
  tls:
    - hosts:
        - admin.example.com
      secretName: admin-dashboard-tls

4단계 — 프록시 우회 차단

IAP는 우회 경로가 하나라도 있으면 무력화됩니다. NetworkPolicy로 업스트림 앱이 ingress 컨트롤러에서 오는 트래픽만 받도록 강제합니다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: admin-dashboard-only-from-ingress
  namespace: apps
spec:
  podSelector:
    matchLabels:
      app: admin-dashboard
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080

여기에 더해, 업스트림 앱은 신원 헤더를 신뢰하기 전에 "이 요청이 정말 프록시에서 왔는가"를 확인해야 합니다. 가장 견고한 방법은 oauth2-proxy의 set-authorization-header로 서명된 ID 토큰(JWT)을 전달받아 앱에서 서명을 검증하는 것입니다. 평문 헤더만 신뢰하는 구조는 내부 네트워크 침해 시 위조 위험이 있습니다.

디바이스 신뢰와 컨텍스트 기반 정책

BeyondCorp 모델의 절반은 디바이스 신뢰입니다. 사용자 인증이 아무리 강해도 악성코드에 감염된 단말이라면 접근을 제한해야 합니다. 실무에서 단계적으로 적용할 수 있는 방법은 다음과 같습니다.

  1. mTLS 클라이언트 인증서 — MDM(Intune, Jamf 등)으로 회사 관리 단말에만 클라이언트 인증서를 배포하고, 프록시에서 TLS 클라이언트 인증을 요구합니다. "관리 단말 여부"를 가장 단순하게 증명하는 방식입니다.
  2. 디바이스 포스처 신호 — EDR/MDM API에서 OS 버전, 디스크 암호화, 화면 잠금 여부를 조회해 정책 엔진에 공급합니다. Cloudflare Access나 Pomerium은 디바이스 포스처 연동을 내장하고 있습니다.
  3. 신뢰 등급(trust tier) — BeyondCorp처럼 디바이스를 fully-trusted, managed, unknown 등급으로 나누고, 애플리케이션 민감도와 매칭합니다. 예를 들어 결제 admin은 fully-trusted 단말에서만, 위키는 managed 이상에서 접근 가능하게 합니다.

nginx 계층에서 mTLS를 요구하는 설정 예시는 다음과 같습니다.

server {
    listen 443 ssl;
    server_name payments-admin.example.com;

    ssl_client_certificate /etc/nginx/tls/device-ca.crt;
    ssl_verify_client on;

    location / {
        auth_request /oauth2/auth;
        # 디바이스 인증서 정보를 업스트림과 감사 로그에 전달
        proxy_set_header X-Device-Cert-DN $ssl_client_s_dn;
        proxy_set_header X-Device-Cert-Verify $ssl_client_verify;
        proxy_pass http://payments-admin.apps.svc.cluster.local:8080;
    }
}

컨텍스트 기반 정책은 "동일한 사용자라도 상황에 따라 다른 결정"을 내리는 것입니다. 대표적인 신호와 정책 예시는 다음과 같습니다.

신호정책 예시
디바이스 등급미관리 단말은 읽기 전용 앱만 허용
위치허용 국가 외 접속 시 step-up 인증(passkey 재인증) 요구
시간업무 시간 외 프로덕션 admin 접근은 승인 워크플로우 필요
인증 강도비밀번호만으로 로그인한 세션은 민감 앱 접근 불가
이상 징후불가능 이동(impossible travel) 감지 시 세션 즉시 폐기

Keycloak에서는 인증 플로우의 조건부 실행기와 step-up authentication(ACR/LoA)으로 인증 강도 정책을 구현할 수 있고, 26.6의 Workflows 기능으로 계정 라이프사이클 자동화(장기 미사용 계정 비활성화 등)를 realm 수준에서 구성할 수 있습니다.

VPN 대체 시나리오

IAP로 VPN을 대체하는 전형적인 시나리오를 정리합니다.

[Before] 모든 직원 --> VPN Gateway --> 사내망 전체 (위키, CI, admin, DB, SSH ...)

[After]
  직원/외주 --> IAP (HTTPS) --> 위키, CI, admin 등 웹 앱        (대부분의 트래픽)
  운영자    --> SSH/DB 전용 솔루션(Boundary, Teleport 등)       (비 HTTP 프로토콜)
  레거시    --> 잔존 VPN (점진적 축소, 접근 대상 서브넷 최소화)

핵심 포인트는 다음과 같습니다.

Cloudflare Access vs Google Cloud IAP vs Pomerium vs oauth2-proxy

항목Cloudflare AccessGoogle Cloud IAPPomeriumoauth2-proxy
형태SaaS (엣지 네트워크)GCP 매니지드오픈소스 + 상용오픈소스
배포 위치Cloudflare 엣지GCP LB 통합자체 호스팅자체 호스팅
IdP 연동모든 OIDC/SAMLGoogle + 외부 IdP 연동모든 OIDC모든 OIDC
디바이스 포스처내장 (WARP 클라이언트)내장 (Endpoint Verification)내장 (Pomerium Zero 포함)없음 (직접 조합 필요)
정책 표현력그룹, 국가, 디바이스 등IAM 조건 + Access LevelsRego/표현식 기반 정교한 정책그룹/이메일 수준
비 HTTP 지원TCP/UDP (Tunnel)TCP forwardingTCP 지원없음
잠금 효과Cloudflare 의존GCP 의존낮음낮음
비용사용자당 과금GCP 요금 내 포함OSS 무료 + 상용 옵션무료

선택 가이드는 다음과 같습니다.

사례 — 내부 admin 도구 보호

실무에서 가장 흔하고 효과가 큰 적용 대상은 내부 admin 도구입니다. Grafana, ArgoCD, Airflow, 자체 개발 백오피스처럼 "인증이 약하거나 별도 계정 체계를 가진" 도구들이 대상입니다.

전형적인 적용 패턴은 다음과 같습니다.

  1. admin 도구의 자체 로그인 화면을 비활성화하거나 헤더/OIDC 인증 모드로 전환합니다. 예를 들어 Grafana는 auth.proxy 설정으로 X-Forwarded-User 헤더를 신뢰하게 할 수 있습니다.
# grafana.ini — IAP 뒤에서 헤더 기반 인증
[auth.proxy]
enabled = true
header_name = X-Forwarded-User
header_property = username
auto_sign_up = true
sync_ttl = 60
# 프록시 IP만 신뢰 (위조 방지의 마지막 보루)
whitelist = 10.0.0.0/8
  1. IAP에서 도구별 허용 그룹을 분리합니다. ArgoCD는 platform 팀, Airflow는 data 팀 그룹만 통과시키는 식입니다. oauth2-proxy를 도구별로 따로 띄우거나, Pomerium처럼 라우트별 정책을 지원하는 프록시를 사용합니다.
  2. 모든 접근을 구조화 로그로 남깁니다. 누가, 언제, 어떤 디바이스로, 어떤 앱에 접근했는지가 요청 단위로 기록되므로 감사 대응이 수월해집니다.
  3. break-glass 경로를 설계합니다. IdP 장애 시 admin 도구에 접근할 비상 절차(임시 정적 자격 증명 + 강한 감사)를 문서화합니다.

단계적 도입 로드맵

Zero Trust는 빅뱅 전환이 아니라 여정입니다. 현실적인 로드맵은 다음과 같습니다.

Phase 0  현황 파악 (1개월)
         - 사내 앱 인벤토리, 인증 방식, 민감도 분류
         - IdP 정비: 그룹/역할 체계, MFA(가능하면 passkey) 활성화

Phase 1  파일럿 (1~2개월)
         - 저위험 앱 2~3개 (위키, 대시보드)를 IAP 뒤로 이전
         - 로그인 UX, 세션 정책, 장애 모드 검증

Phase 2  확장 (3~6개월)
         - admin 도구 전체 + 신규 앱은 기본 IAP 적용
         - 디바이스 인증서(mTLS) 배포, 신뢰 등급 도입
         - 비 HTTP 접근(SSH/DB)에 Teleport 등 도입

Phase 3  VPN 축소 (6~12개월)
         - VPN 접근 대상을 레거시 시스템으로 한정
         - 컨텍스트 기반 정책(위치, 시간, step-up) 고도화
         - 정책의 코드화(IaC)와 정기 접근 리뷰 자동화

Phase 4  지속 개선
         - 세션 이상 탐지, 위험 기반 재인증
         - 워크로드 identity(SPIFFE)와 사용자 identity 정책 통합

각 단계에서 측정할 지표도 함께 정의하는 것이 좋습니다. VPN 동시 접속 수, IAP 뒤로 이전된 앱 비율, 평균 온보딩/오프보딩 시간, 인증 관련 헬프데스크 티켓 수가 대표적입니다.

2026년 관점 — AI agent 접근 제어까지

2026년의 새로운 과제는 사람이 아닌 주체, 특히 AI agent의 내부 시스템 접근입니다. agent가 사내 위키를 검색하고 admin API를 호출하는 시나리오가 일상화되면서, IAP의 정책 대상이 "사용자 + 디바이스"에서 "사용자 + 디바이스 + agent"로 확장되고 있습니다.

실무적으로 정리하면 다음과 같습니다.

BeyondCorp가 "위치에서 신원으로"의 전환이었다면, 지금은 "사람의 신원에서 모든 주체(human + non-human)의 신원으로"의 전환이 진행 중입니다. IAP를 도입할 때 정책 모델을 사람 전용으로 좁게 설계하지 말고, 주체(subject) 추상화를 넓게 잡아 두는 것이 2026년의 설계 포인트입니다.

운영 베스트 프랙티스와 안티패턴

마지막으로 운영 관점의 체크리스트를 정리합니다.

베스트 프랙티스

안티패턴

마치며

Identity-Aware Proxy는 Zero Trust를 가장 빠르게 체감할 수 있는 구현체입니다. oauth2-proxy + Keycloak + nginx auth_request라는 검증된 오픈소스 조합만으로도 BeyondCorp의 핵심인 "모든 요청에 인증과 인가"를 오늘 바로 시작할 수 있습니다. 중요한 것은 도구가 아니라 원칙입니다. 위치가 아닌 신원, 세션이 아닌 요청, 정적 규칙이 아닌 컨텍스트. 그리고 2026년에는 그 신원의 범위가 AI agent까지 넓어졌다는 점을 설계 초기부터 반영하시기 바랍니다.

다음 글에서는 IAP 뒤편, 즉 API Gateway와 서비스 메시 계층에서의 토큰 검증(Istio, Envoy)을 다룹니다.

참고 자료

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다