[Kotlin] HttpServletRequest.getRemoteAddr()를 사용하여 클라이언트 IP 주소 가져오기
웹 애플리케이션을 개발하다 보면 클라이언트의 IP 주소가 필요한 경우가 많습니다. 사용자 인증, 로깅, 통계 수집, 지역 기반 콘텐츠 제공 등 다양한 상황에서 IP 주소를 활용할 수 있습니다. Spring Boot와 Kotlin을 사용하여 클라이언트의 IP 주소를 가져오는 방법에 대해 알아보겠습니다.
기본 방법: HttpServletRequest.getRemoteAddr()
가장 기본적인 방법은 HttpServletRequest
의 getRemoteAddr()
메소드를 사용하는 것입니다. 이 메소드는 클라이언트의 IP 주소를 문자열로 반환합니다.
@RestController
class IpController {
@GetMapping("/ip")
fun getClientIp(request: HttpServletRequest): String {
val clientIp = request.remoteAddr
return "Your IP address is: $clientIp"
}
}
위 코드에서는 HttpServletRequest
를 컨트롤러 메소드에 매개변수로 주입받아 remoteAddr
속성(Kotlin에서는 프로퍼티 구문 사용)을 통해 IP 주소를 가져옵니다.
프록시 문제와 X-Forwarded-For 헤더
그러나 실제 프로덕션 환경에서는 대부분 프록시 서버, 로드 밸런서, CDN 등을 통해 요청이 전달됩니다. 이 경우 getRemoteAddr()
는 중간 프록시 서버의.IP 주소를 반환하며, 실제 클라이언트의 IP 주소가 아닐 수 있습니다.
이 문제를 해결하기 위해 대부분의 프록시는 원래 클라이언트의 IP 주소를 X-Forwarded-For
헤더에 포함시킵니다. 따라서 더 정확한 IP 주소를 가져오기 위해 다음과 같은 유틸리티 함수를 만들 수 있습니다:
@Component
class IpUtil {
fun getClientIp(request: HttpServletRequest): String {
var ip = request.getHeader("X-Forwarded-For")
if (ip.isNullOrEmpty() || "unknown".equals(ip, ignoreCase = true)) {
ip = request.getHeader("Proxy-Client-IP")
}
if (ip.isNullOrEmpty() || "unknown".equals(ip, ignoreCase = true)) {
ip = request.getHeader("WL-Proxy-Client-IP")
}
if (ip.isNullOrEmpty() || "unknown".equals(ip, ignoreCase = true)) {
ip = request.getHeader("HTTP_CLIENT_IP")
}
if (ip.isNullOrEmpty() || "unknown".equals(ip, ignoreCase = true)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR")
}
if (ip.isNullOrEmpty() || "unknown".equals(ip, ignoreCase = true)) {
ip = request.remoteAddr
}
// X-Forwarded-For 헤더가 여러 IP를 포함할 수 있으므로, 첫 번째 IP만 추출
val commaIndex = ip?.indexOf(',') ?: -1
if (commaIndex != -1) {
ip = ip?.substring(0, commaIndex)
}
return ip ?: "0.0.0.0"
}
}
이 유틸리티 클래스를 컨트롤러에서 다음과 같이 사용할 수 있습니다:
@RestController
class IpController(private val ipUtil: IpUtil) {
@GetMapping("/ip")
fun getClientIp(request: HttpServletRequest): String {
val clientIp = ipUtil.getClientIp(request)
return "Your IP address is: $clientIp"
}
}
Spring Boot의 RemoteIpValve 구성
Spring Boot에서는 이런 프록시 관련 문제를 해결하기 위해 Tomcat의 RemoteIpValve
를 구성할 수 있습니다. application.properties
또는 application.yml
에서 다음과 같이 설정할 수 있습니다:
application.properties
server.tomcat.remote-ip-header=X-Forwarded-For
server.tomcat.protocol-header=X-Forwarded-Proto
application.yml
server:
tomcat:
remote-ip-header: X-Forwarded-For
protocol-header: X-Forwarded-Proto
이렇게 설정하면 Tomcat이 X-Forwarded-For
헤더에서 IP 주소를 자동으로 가져와 request.remoteAddr
에 설정해 줍니다.
전역적으로 로깅하기: 인터셉터 사용
클라이언트 IP 주소를 모든 요청에 대해 로깅하고 싶다면, 인터셉터를 사용할 수 있습니다:
@Component
class IpLoggingInterceptor(private val ipUtil: IpUtil) : HandlerInterceptor {
private val logger = LoggerFactory.getLogger(IpLoggingInterceptor::class.java)
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
val clientIp = ipUtil.getClientIp(request)
val requestURI = request.requestURI
logger.info("Request from IP: $clientIp, URI: $requestURI")
return true
}
}
그리고 이 인터셉터를 등록합니다:
@Configuration
class WebConfig(private val ipLoggingInterceptor: IpLoggingInterceptor) : WebMvcConfigurer {
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(ipLoggingInterceptor)
.addPathPatterns("/**") // 모든 경로에 적용
}
}
보너스: IP 주소 저장하기
사용자 활동을 로깅하거나 분석하기 위해 IP 주소를 데이터베이스에 저장해야 할 수도 있습니다. 간단한 JPA 엔티티와 Repository를 사용한 예시입니다:
@Entity
data class UserActivity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
val ipAddress: String,
val requestUri: String,
val timestamp: LocalDateTime = LocalDateTime.now()
)
interface UserActivityRepository : JpaRepository<UserActivity, Long>
그리고 이를 서비스에서 사용합니다:
@Service
class UserActivityService(private val userActivityRepository: UserActivityRepository) {
fun logUserActivity(ipAddress: String, requestUri: String) {
val activity = UserActivity(
ipAddress = ipAddress,
requestUri = requestUri
)
userActivityRepository.save(activity)
}
}
인터셉터에서 이 서비스를 호출하면 모든 요청에 대한 IP 주소를 로깅할 수 있습니다:
@Component
class IpLoggingInterceptor(
private val ipUtil: IpUtil,
private val userActivityService: UserActivityService
) : HandlerInterceptor {
private val logger = LoggerFactory.getLogger(IpLoggingInterceptor::class.java)
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
val clientIp = ipUtil.getClientIp(request)
val requestURI = request.requestURI
logger.info("Request from IP: $clientIp, URI: $requestURI")
// 특정 경로에 대해서만 DB에 로깅 (API 요청만 로깅)
if (requestURI.startsWith("/api/")) {
userActivityService.logUserActivity(clientIp, requestURI)
}
return true
}
}
결론
Spring Boot와 Kotlin에서 클라이언트 IP 주소를 가져오는 방법에는 여러 가지가 있습니다. 가장 간단한 방법은 HttpServletRequest.getRemoteAddr()
를 사용하는 것이지만, 프록시 환경에서는 X-Forwarded-For
헤더를 고려해야 합니다.
실제 프로덕션 환경에서는 프록시 서버, 로드 밸런서 등 다양한 중간 계층이 존재하므로, 이러한 상황을 고려하여 클라이언트 IP 주소를 정확하게 가져오는 로직을 구현해야 합니다. Spring Boot의 설정을 통해 이러한 문제를 간단히 해결할 수도 있습니다.
클라이언트 IP 주소는 보안, 지역화, 사용자 분석 등 다양한 목적으로 활용될 수 있으므로, 정확한 IP 주소를 가져오는 것이 중요합니다.
'개발&프로그래밍' 카테고리의 다른 글
[Cursor] AI로 코딩하는 시대, Cursor AI 소개 (0) | 2025.04.07 |
---|---|
[N8N] 개발자라면 반드시 써봐야 할 노코드 자동화 툴, N8N 완전 정복 가이드 (0) | 2025.04.07 |
[HTTP] HTTP 주요 헤더와 활용법 (0) | 2025.02.21 |
[HTTP] HTTP 인증방식 : Basic, Bearer, OAuth까지 (0) | 2025.02.21 |
[HTTP] 웹 브라우저 동작 원리 : URL 입력부터 화면 표시까지 (0) | 2025.02.21 |
댓글