티스토리 뷰
반응형
스프링 시큐리티 완전 처음부터 12편 (Kotlin)
운영에서 진짜 중요한 것들: 보안 테스트 · 로깅 · 모니터링 (안 터지게 만드는 기술)
이제 구현은 끝났다.
근데 실무에서는 여기서 질문이 바뀐다.
“이거… 운영에서 안 터질까요?”
“누가 공격하면 어디서 확인하죠?”
“권한 오류, 인증 실패… 로그로 남나요?”
이번 글은 기능 추가가 아니라 ‘운영 안정성’ 이야기다.
솔직히 말하면, 이 파트가 빠진 보안 구현은 반쪽짜리다.
0️⃣ 이 글에서 다룰 것 (실무 필수)
- 보안 테스트 전략 (단위 / 통합 / 시나리오)
- 인증·인가 실패 로그 설계
- JWT 오류를 “사고 징후”로 바꾸는 방법
- 운영에서 꼭 필요한 모니터링 포인트
- 최소 비용으로 시작하는 실전 세팅
1️⃣ “보안 테스트요? 로그인만 되면 되는 거 아닌가요?”
❌ 이 생각이 제일 위험하다.
보안 테스트는 “잘 되는지”가 아니라
👉 **“안 될 때, 제대로 막히는지”**를 보는 테스트다.
2️⃣ 꼭 만들어야 하는 보안 테스트 시나리오 TOP 6
✅ 1. 인증 없이 보호 API 접근
GET /private/hello
기대 결과
- HTTP 401
- 컨트롤러 로직 진입 ❌
✅ 2. 잘못된 JWT로 접근
Authorization: Bearer invalid.jwt.token
기대 결과
- HTTP 401
- SecurityContext 비어 있음
✅ 3. 만료된 JWT
- exp 지난 토큰
기대 결과
- HTTP 401
- 로그에 token expired
✅ 4. ROLE 없는 사용자 → 관리자 API
GET /admin/hello
기대 결과
- HTTP 403
- 인증은 성공, 인가에서 차단
✅ 5. 다른 tenantId로 데이터 접근
GET /contracts/{otherTenantId}
기대 결과
- HTTP 403 또는 데이터 없음
- 절대 다른 테넌트 데이터 노출 ❌
✅ 6. 로그인 연속 실패 (브루트포스 징후)
반응형
- 같은 IP
- 같은 계정
- 짧은 시간 내 다수 실패
기대 결과
- 로그에 명확히 남음
- (선택) 계정 잠금 or 알림
3️⃣ Spring Security 테스트 코드 기본 패턴 (Kotlin)
3-1. 인증 없는 요청 테스트
@Test
fun `인증 없이 보호 API 접근 시 401`() {
mockMvc.get("/private/hello")
.andExpect {
status { isUnauthorized() }
}
}
3-2. ROLE 테스트
@Test
@WithMockUser(username = "user", roles = ["USER"])
fun `USER 는 ADMIN API 접근 불가`() {
mockMvc.get("/admin/hello")
.andExpect {
status { isForbidden() }
}
}
📌 이 테스트는
**“보안이 깨졌을 때 바로 알려주는 안전장치”**다.
4️⃣ 인증·인가 실패 로그, 이렇게 남겨야 의미 있다
❌ 흔한 로그 (의미 없음)
Authentication failed
⭕ 실무에서 쓸 로그
[AUTH_FAIL]
ip=203.0.113.10
username=test
reason=INVALID_TOKEN
path=/admin/hello
👉 누가 / 어디서 / 왜
이 3가지가 무조건 있어야 한다.
5️⃣ JWT 필터에 로깅 포인트 추가 (실전 코드)
if (!jwtProvider.validateToken(token)) {
log.warn(
"[AUTH_FAIL] ip={}, token=invalid, path={}",
request.remoteAddr,
request.requestURI
)
}
이 로그는 나중에:
- 공격 탐지
- 장애 분석
- 고객 문의 대응
전부에 쓰인다.
6️⃣ 인증/인가 예외를 “의도된 응답”으로 만들기
6-1. AuthenticationEntryPoint (401)
@Component
class JwtAuthEntryPoint : AuthenticationEntryPoint {
override fun commence(
request: HttpServletRequest,
response: HttpServletResponse,
authException: AuthenticationException
) {
response.status = HttpServletResponse.SC_UNAUTHORIZED
response.writer.write("""{"message":"unauthorized"}""")
}
}
6-2. AccessDeniedHandler (403)
@Component
class JwtAccessDeniedHandler : AccessDeniedHandler {
override fun handle(
request: HttpServletRequest,
response: HttpServletResponse,
accessDeniedException: AccessDeniedException
) {
response.status = HttpServletResponse.SC_FORBIDDEN
response.writer.write("""{"message":"forbidden"}""")
}
}
👉 프론트엔드는 이제
401 / 403 기준으로 명확한 UX 분기 가능.
7️⃣ 운영에서 꼭 봐야 하는 보안 지표 5가지

📊 필수 지표
- 인증 실패 횟수 (401)
- 인가 실패 횟수 (403)
- JWT 만료 오류 빈도
- 로그인 실패 Top IP
- 관리자 API 접근 시도 로그
👉 이 중 하나라도 갑자기 튀면
보안 이슈 or 버그다.
8️⃣ 최소 비용으로 시작하는 운영 세팅
✔ 로그
- Logback
- JSON 로그 (선택)
- 일단 파일만이라도 남겨라
✔ 모니터링
- Spring Actuator
- Micrometer
- Prometheus + Grafana (나중에)
✔ 알림 (선택)
- 슬랙 Webhook
- 관리자 로그인 실패 다수 발생 시 알림
9️⃣ 실무에서 진짜 많이 터지는 운영 사고
❌ 보안 예외를 전부 500으로 반환
→ 프론트도, 운영도 지옥
❌ 인증 실패 로그 없음
→ 공격인지 버그인지 구분 불가
❌ tenant 사고를 로그로 못 찾음
→ 대응 지연 = 신뢰 하락
🔚 이번 편 핵심 요약
- 보안은 테스트 + 로깅 + 모니터링 까지가 한 세트
- “안 되는 경우”를 먼저 테스트해야 한다
- JWT 오류는 장애 신호다
- 운영 로그는 “사람이 읽을 수 있게” 남겨라
다음 글 예고 (번외, 진짜 마지막)
**보안 사고 시나리오 리허설:
“토큰 유출 / 권한 오류 / 테넌트 사고가 났다면?”**
- 사고 유형별 대응 플로우
- 로그로 원인 찾는 법
- 서비스 중단 없이 수습하는 전략
- 사후 개선 포인트 정리
👉 이 글까지 보면 ‘운영 가능한 시큐리티’ 완성
스프링시큐리티, springsecurity, 코틀린, kotlin, jwt, 보안테스트, 모니터링, 로그설계, 백엔드개발, 운영보안
'study > kotlin' 카테고리의 다른 글
| 스프링 시큐리티 완전 처음부터 14편 (Kotlin) (0) | 2026.01.07 |
|---|---|
| 스프링 시큐리티 완전 처음부터 13편 (Kotlin) (0) | 2026.01.06 |
| 스프링 시큐리티 완전 처음부터 11편 (Kotlin) (0) | 2026.01.04 |
| 스프링 시큐리티 완전 처음부터 9편 (Kotlin) (0) | 2025.12.31 |
| 스프링 시큐리티 완전 처음부터 8편 (Kotlin) (0) | 2025.12.30 |
※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- Python
- fastapi
- seo 최적화 10개
- 압박면접
- NestJS
- flax
- CI/CD
- rag
- Redis
- Prisma
- 웹개발
- PostgreSQL
- kotlin
- llm
- 개발블로그
- JAX
- REACT
- 프론트엔드개발
- Next.js
- DevOps
- 딥러닝
- node.js
- ai철학
- Express
- JWT
- nextJS
- Docker
- 쿠버네티스
- 백엔드개발
- SEO최적화
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
글 보관함
반응형

