안녕하세요. Web Frontend팀 이민하입니다. 지난 빅스마일데이에 첫 론칭한 꿀템 피드 서비스! 이 서비스를 만들기 위한 여정을 여러분께 소개드리려고 합니다. Intro 시작은 디지털 빅세일이 끝난 2월 말.. 저희 팀장님의 한마디에서 시작했습니다. "챗GBT를 우리만 보기에 아깝지 않아.....
기술 블로그 모음
국내 IT 기업들의 기술 블로그 글을 한 곳에서 모아보세요


안녕하세요. LINE VOOM AI 조직의 서버 개발자 박찬우, 양유성입니다. 이번 글의 제목은 'Massive AI Real-time Embedding을 효율적으로 다루기'입니다...

AI 비즈니스의 지속 가능성을 위해 초기 비용 예산을 신중하게 고려하고, AI 서비스의 구축 방법별 비용 요인을 비교하며, 효과적인 예산 관리와 비용 절감 전략을 제시합니다.
4. 홈 어떤 과정으로 만들어졌나 4-…
이제는 여러 사용자분들이 익숙해지셨을 뱅크샐러드의 홈 화면, 그렇지만 2년 전의 뱅크샐러드에는 놀랍게도 ‘홈’이 없었다. 지금으로부터 2년 전인 202…
5. 출시 다음에는 ‘개선과 운영’ 홈탭을 출시하는 과정에서 팀 내에 많은 변동이 있었다. 커리어 개발・창업・이민 준비 등 다양한 이유로 팀원들은 회사를 떠났다. 초기 기획을 함께 이끌어주던 디자이너 동료도 떠나게 되었고, PM…

안녕하세요 사람인 SRE팀 박형규입니다. 지난 포스팅과(사이트 신뢰성에 대한 지표는 어떻게 구성할까?) 다르게 이번엔, AWS EKS 환경을 좀 더 안정적이며 확장성 있게 운영하기 위해 고민하고 테스트 했던 내용에 대해 공유 드리고자 합니다. 사람인은 K8S 플랫폼으로 On-Premise가 주이고 최근 서비스는 AWS EKS를 사용하고 있습니다. 초기...

안녕하세요! 올리브영 주문 & 결제를 담당하고 있는 빈센트입니다. 얼마 전, 2024년 5월 31일부터 6월…

2023년 미국의 정부기관에서는 다수의 정보보안 사고가 발생해 수많은 개인정보가 노출되었습니다. 취약해진 시스템과 개인정보 노출/탈취가 원인으로 지목되어 더 나은 보안 강화가 필요한 상황입니다.

LINE 개발 조직에서는 성숙한 개발 문화를 만들기 위해 다양한 시도를 하고 있습니다. 클라이언트 앱 품질을 향상시키기 위해 개발 프로세스를 개선하고 있는 LY Mobile Dev...

안녕하세요, SRE 팀의 Ken.K입니다. Hyperconnect의 Azar 서비스는 전 세계에서 서비스를 하고 있기 때문에 국가별로 서버 메트릭이나 클라이언트에서 수집하고 있는 이벤트 데이터를 기반으로 모니터링하고 있습니다. 따라서 대부분의 국가별 이슈를 준 실시간으로 마주하고 대응해나가고 있습니다. 때는 2024년 1월 어느 날, 내부 CS 논의 ...

2024년 4월, 삼성SDS가 공공 고객을 대상으로 진행한 「삼성SDS Gen AI Day」 세미나 중, ‘공공 디지털 혁신을 위한 도약: 초거대 AI 및 클라우드 동향’과 ‘디지털 플랫폼 정부를 위한 클라우드: Samsung Cloud Platform’ 내용을 기반으로 작성했습니다.

금융 디지털 혁신인 핀테크의 현주소와 앞으로 AI가 금융을 어떻게 혁신하고 새로운 사용자 경험과 시장을 창출하는지 살펴봅니다.

들어가며 안녕하세요. AI Services Lab 팀의 ML 엔지니어 박희웅입니다. 저희 팀에서는 오픈챗과 관련된 다양한 AI/ML 모델을 개발해 서빙하고 있는데요. 앞서 오프라인...

안녕하세요. 네이버페이 회원&인증BE 의 최용화입니다.Spring Security는 강력한 보안 프레임워크로서, 애플리케이션의 인증과 인가 과정을 효율적으로 관리합니다. 저희 팀에서는 다음과 같은 기능을 구현하는 데에 Spring Security 를 사용하고 있습니다.인증 여부 확인 (인증이 안된 사용자일 경우 로그인 및 가입 유도 / 인증이 완료된 사용자일 경우 적절한 권한 부여), 권한을 이용한 접근 제어(권한이 없는 사용자일 경우 권한 획득을 위한 절차 수행), 보안 공격으로부터 보호(CSRF 공격 방지), PC / MOBILE 최적화 페이지 제공(사용자가 접근한 환경을 파악하여 최적화된 페이지 제공)이 글에서는 Spring Security의 인증(Authentication) 과정 전반을 살펴보고, 각 단계의 역할과 작동 방식을 자세히 알아보겠습니다.이 글은 Spring Security 6.3.0 공식문서를 기반으로 작성되었습니다.Spring Security 의 Filter 기반 동작 방식 이해Spring Security의 인증 수행을 이해하려면 Spring Security 의 구조에 대한 이해가 선행되어야 합니다. Spring Security는 Servlet Filter 기반으로 동작합니다. 여기서, 중요한 개념인 FilterChainProxy, SecurityFilterChain, 보안 필터(Security Filter)에 대해 알아봅니다.FilterChainProxy의 개념FilterChainProxy는 Spring Security에서 제공하는 특수한 Filter로 SecurityFilterChain을 사용하여 다양한 보안 필터가 동작하게 합니다.사실, Servlet Container의 라이프사이클과 Spring의 ApplicationContext 사이를 연결하는 DelegatingFilterProxy 라는 필터 구현체도 중요한 상위 개념이나 이 글에서는 설명을 생략합니다. 자세한 설명은 이 페이지를 참고하세요.SecurityFilterChain의 개념SecurityFilterChain은 Spring Security에서 보안 필터(Security Filter)의 체인을 정의하는 데 사용됩니다. 요청이 애플리케이션의 Servlet에 도달하기 전에 다양한 보안 검사를 수행하는 필터들이 있으며, 이를 보안 필터라고 부릅니다. SecurityFilterChain은 각 보안 필터가 순차적으로 실행되도록 하여 애플리케이션의 보안 설정을 체계적으로 관리할 수 있게 합니다.보안 필터(Security Filter)의 주요 기능SecurityFilterChain 에 선언된 다양한 보안 필터를 통해 아래의 기능을 수행하게 됩니다.인증(Authentication): 사용자의 신원을 확인합니다. 예를 들어, 사용자가 로그인 폼을 제출하면, 이를 처리하는 필터가 실행됩니다.인가(Authorization): 사용자가 요청한 리소스에 접근할 권한이 있는지 확인합니다.각종 보안 공격으로부터 보호(Protection Against Exploits): CSRF 공격, Session Fixation 공격, sniffing 공격, Clickjacking 등의 보안 공격으로부터 보호합니다.세션 관리: 사용자의 세션을 생성, 관리, 종료하는 과정입니다.기타 기능: HTTP 응답 헤더를 설정하여 보안을 강화하는 기능, Remember Me 기능 등을 지원합니다.보안 필터(Security Filter) 소개여러 개의 보안 필터가 있지만, 이 글에서 자주 보게 될 몇 가지 보안 필터만 가볍게 소개 드리려고 합니다. 나열된 순서대로 실행됩니다.UsernamePasswordAuthenticationFilter: 폼 기반 로그인 처리를 수행합니다.DefaultLoginPageGeneratingFilter: 기본 로그인 페이지를 생성합니다.ExceptionTranslationFilter: ExceptionTranslationFilter 의 다음 Filter 에서 발생한 Exception을 처리하고 이에 대한 적절한 응답을 반환합니다.AuthorizationFilter: 사용자가 요청한 리소스에 대해 접근 권한이 있는지 확인합니다. 권한이 없는 경우 접근을 거부하고, 적절한 에러 페이지를 반환하거나 예외를 발생시킵니다.Spring Security의 Form 기반 인증인증(Authentication)은 특정 리소스에 액세스하려는 주체(Principal)의 신원을 확인하는 과정입니다. Spring Security는 다양한 인증 방법을 지원하며, 이 글에서는 주로 폼 기반 인증(Form-Based Authentication)을 예로 들어 설명하겠습니다.인증 관련 주요 용어Spring Security 인증과 관련하여 자주 사용되는 용어에 대해 설명합니다.SecurityContextHolder: Spring Security가 인증된 사용자의 정보를 저장하는 곳입니다.SecurityContext: SecurityContextHolder에서 가져오며 현재 인증된 사용자의 인증정보(Authentication)를 포함합니다.Authentication: 사용자가 입력한 자격 증명(Pricipal과 Credentials)을 AuthenticationManager에 전달하는 용도로 사용되거나 SecurityContext에서 현재 사용자를 나타내는 용도로 사용되는 객체입니다.GrantedAuthority(Authorities): 인증된 사용자에게 부여된 권한을 나타내며, 역할(role)이나 범위(scope) 등을 포함합니다.AuthenticationManager: Spring Security의 필터가 인증을 수행하는 방법을 정의한 API(인터페이스)입니다.ProviderManager: AuthenticationManager 의 구현체입니다.AuthenticationProvider: ProviderManager가 여러 종류의 인증(Basic 인증, Form 인증 등)을 지원 및 수행하기 위해 사용하는 인터페이스입니다. 하나의 ProviderManager에 여러 개의 AuthenticationProvider를 등록하여 사용할 수 있습니다. 가장 흔히 사용되는 구현체는 DaoAuthenticationProvider입니다.Form 기반 인증 수행 과정 — UsernamePasswordAuthenticationFilter아래는 UsernamePasswordAuthenticationFilter 에서 수행하는 인증 과정에 대한 도식입니다.Form 기반 인증 요청에서 username과 password를 추출하여 UsernamePasswordAuthenticationToke 객체를 ProviderManager에 전달합니다. (여기서, UsernamePasswordAuthenticationToken은 위에서 설명한 Authentication 인터페이스의 구현체이고, ProviderManager는 위에서 설명한 AuthenticationManager 인터페이스의 구현체입니다.)ProviderManager는 DaoAuthenticationProvider 를 이용하여 인증을 수행합니다. (여기서, DaoAuthenticationProvider 는 위에서 설명한 AuthenticationProvider 인터페이스의 구현체입니다.)DaoAuthenticationProvider는 UserDetailsService를 이용해 전달받은 username과 일치하는 UserDetails(저장된 사용자 정보)를 조회합니다.DaoAuthenticationProvider는 PasswordEncoder를 이용해 전달받은 password와 3번 과정에서 조회한 UserDetails의 비밀번호가 일치하는지 검증합니다.4번 과정에서 비밀번호 검증까지 성공하면 사용자 인증은 성공한 것입니다. 이 때, 인증이 완료된 UsernamePasswordAuthenticationToken 을 반환하게 되며 이 구현체의 principal 값은 UserDetailsService에서 조회해온 UserDetails로 설정됩니다.최종적으로, 반환된 UsernamePasswordAuthenticationToken은 SecurityContextHolder에 설정됩니다.Form 기반 인증 수행 예시 — 예제 코드인증 수행 과정을 Spring Security 예제 코드와 TRACE 로깅을 통해 확인해봅니다.먼저, Gradle 의존성부터 설정합니다.dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web'}리소스를 관리하는 ResourceController.java 입니다.@Controllerpublic class ResourceController { @GetMapping("/private") @ResponseBody public String loginSuccess() { return "Private Resource"; }}우리가 보호하려는 리소스에 대한 핸들러를 간단하게 명시하였습니다.다음으로 Spring Security 에서 제공하는 Form 기반 인증을 구성한 SecurityConfig.java 입니다.@EnableWebSecurity@Configurationpublic class SecurityConfig { @Bean public UserDetailsService userDetailsService() { InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(); UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("12345") .authorities("READ") .build(); inMemoryUserDetailsManager.createUser(user); return inMemoryUserDetailsManager; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(authorize -> { authorize .requestMatchers("/private").hasAuthority("READ") .anyRequest().authenticated(); }); http.formLogin(Customizer.withDefaults()); return http.build(); }}이 구성에서 설정한 내용은 아래와 같습니다.사용자 정보 저장을 위해 메모리 저장소를 이용하며 사용자 정보를 추가해두었습니다. 사용자의 정보는 다음과 같습니다.username: userpassword: 12345권한: READ2. 보호할 리소스인 http://localhost:8080/private 리소스에 접근하기 위해서는 인증된 사용자여야 하며, 인증된 사용자가 가지는 권한 중 READ 권한이 있어야 함을 명시하였습니다.3. Spring Security 에서 기본적으로 제공하는 Form 기반 인증을 수행합니다.다음으로 Spring Security 에서 제공하는 로깅 기능을 명시한 application.properties 파일입니다.logging.level.org.springframework.security=TRACESecurity Filter가 수행되는 순서와 사용자 요청이 어떻게 처리되는지 로그를 통해 확인할 수 있습니다.Form 기반 인증 수행 예시 — 인증되지 않은 사용자가 리소스를 요청할 때흐름도인증되지 않은 사용자가 http://localhost:8080/private 리소스에 접근을 시도합니다.AuthorizationFilter에서 AccessDeniedException을 발생시켜 인증되지 않은 요청이 거부되었음을 알립니다.ExceptionTranslationFilter는 AuthorizationFilter 에서 발생한 AccessDeniedException 에 대한 처리로 아래의 과정을 수행합니다.SecurityContextHolder 에 저장된 Authentication 데이터가 지워집니다.추후에 인증 과정이 성공할 때, 현재 실패한 http://localhost:8080/private 요청을 바로 수행할 수 있도록 현재의 요청 정보가 담긴 HttpServletRequest 객체를 RequestCache에 저장해둡니다.AuthenticationEntryPoint에 구현된 인증되지 않은 사용자에게 자격 증명을 요청하는 기능을 수행합니다. 여기서 구현된 AuthenticationEntryPoint 객체는 LoginUrlAuthenticationEntryPoint 이므로 로그인 페이지(기본 설정 값은 http://localhost:8080/login)로 redirect 하는 작업을 수행하게 됩니다.4. 사용자의 브라우저는 redirect된 로그인 페이지 (기본 설정 값은 http://localhost:8080/login) 를 요청하게 됩니다.5. LoginController 에서 로그인 페이지(login.html)를 렌더링하여 응답합니다.로그 확인위 로그는 흐름도 상 1 ~ 2번까지의 과정에 대한 로그입니다. 인증되지 않은 사용자가 http://localhost:8080/private 요청을 호출할 때 발생하는 로그입니다. 로그를 통해 아래와 같은 사실을 알 수 있습니다.FilterChainProxy를 통해 15개의 보안 필터(Security Filter)가 수행됩니다.8번째 보안 필터로 UsernamePasswordAuthenticationFilter가 수행되지만, 로그인 요청(POST /login)이 아니기 때문에 실질적인 인증 과정은 수행되지 않습니다. Spring Security 내부 구현은 아래와 같습니다.UsernamePasswordAuthenticationFilter — 로그인 요청(POST /login) 이 아닐 경우, 인증 로직을 수행하지 않고 다음 보안 필터 호출UsernamePasswordAuthenticationFilter — 로그인 요청(POST /login) 인지 확인15번째 보안 필터로 AuthorizationFilter가 수행되지만, /private 리소스에 접근하려는 사용자가 인증되지 않은 사용자이기 때문에 AccessDeniedException 을 던집니다.14번째 보안 필터인 ExceptionTranslationFilter 는 15번째 보안 필터인 AuthorizationFilter에서 던지는 AcessDeniedException 을 잡아 이전 절에서 설명한 예외 처리 로직을 수행합니다. Spring Security 내부 구현은 아래와 같습니다.ExceptionTranslationFilter — 다음 보안 필터 수행 중 예외 발생 시, handleSpringSecurityException 메소드 호출handleSpringSecurityException 메소드 — 발생한 예외가 AccessDeniedException 일 경우, handleAceessDeniedException 메소드 호출handleAceessDeniedException 메소드 — 인증 정보가 AnonymousAuthentication이므로 sendStartAuthentication 메소드 호출SecurityContext 제거 / 기존 요청 저장 / 로그인 진입점으로 이동위 로그는 흐름도 상 3 ~ 5번까지의 과정에 대한 로그입니다.로그를 통해 아래와 같은 사실을 알 수 있습니다.14번째 보안 필터인 ExceptionTranslationFilter 에 의해 아래 작업이 수행됩니다.현재의 요청 정보가 담긴 HttpServletRequest 객체를 HttpSessionRequestCache에 저장해둡니다.AuthenticationEntryPoint 에 의해 로그인 페이지(http://localhost:8080/login)로 redirect 하게 됩니다.사용자가 인증을 위해 로그인 페이지(http://localhost:8080/login)를 요청하면, 9번째 보안 필터인 DefaultLoginPageGeneratingFilter 에 의해 기본 설정된 로그인 페이지를 응답합니다. Spring Security 내부 구현은 아래와 같습니다.DefaultLoginPageGeneratingFilter — 기본 로그인 페이지 생성 및 응답Form 기반 인증 수행 예시 — 인증 과정에서 실패할 때흐름도사용자가 username과 password를 제출하면 UsernamePasswordAuthenticationFilter는 HttpServletRequest 객체에서 username과 password를 추출하여 Authentication의 구현체인 UsernamePasswordAuthenticationToken을 생성합니다.다음으로 UsernamePasswordAuthenticationToken이 인증을 위해 AuthenticationManager 인스턴스로 전달됩니다.인증이 실패하면 아래 과정을 수행합니다.SecurityContextHolder 에 저장된 Authentication 데이터가 지워집니다.RememberMeServices.loginFail() 메소드가 호출됩니다. RememberMeService 를 설정하지 않은 경우, 어떤 작업도 수행되지 않습니다. 이 예제에서는 RememberMeService 기능을 별도로 설정하지 않았기 때문에 어떤 작업도 수행되지 않습니다.AuthenticationFailureHandler 에 구현된 onAuthenticationFailure() 메소드를 수행합니다. 기본적으로 설정되어 있는 AuthenticationFailureHandler의 구현체는 SimpleUrlAuthenticationFailureHandler 입니다. 인증이 실패하면 /login?error URL로 redirect합니다. 로그인 페이지에서는 error 파라미터의 값을 사용하여 인증 실패 메시지를 사용자에게 표시할 수 있습니다.로그 확인위 로그는 흐름도 상 1 ~ 2번까지의 과정에 대한 로그입니다.인증되지 않은 사용자가 POST http://localhost:8080/login 요청을 잘못된 인증 정보와 함께 전송 시, 발생하는 로그입니다. 로그를 통해 아래와 같은 사실을 알 수 있습니다.8번째 보안 필터로 UsernamePasswordAuthenticationFilter가 수행되고 ProviderManager 와 DaoAuthenticationProvider 가 순차적으로 수행되며 인증이 수행됩니다. Spring Security 내부 구현은 아래와 같습니다.UsernamePasswordAuthenticationFilter — AuthenticationManager 의 authenticate 메소드 호출AuthenticationManager 의 authenticate 메소드 — AuthenticationProvider의 authenticate 메소드 호출AuthenticationProvider의 authenticate 메소드 — 제출된 username과 일치하는 사용자 정보가 없어 BadCredentialException 메소드 호출위 로그는 흐름도 상 3번 과정에 대한 로그입니다. 로그를 통해 아래와 같은 사실을 알 수 있습니다.인증 실패 시, 아래 작업을 수행합니다.SecurityContextHolder 에 저장된 Authentication 데이터가 지워집니다.SimpleUrlAuthenticationFailureHandler 에 구현된 onAuthenticationFailure() 메소드를 수행합니다. /login?error URL로 redirect합니다. Spring Security 내부 구현은 아래와 같습니다.AuthenticationFailureHandler 의 onAuthenticationFailure 메소드 — 인증 실패 시, 로그인 페이지로 redirectForm 기반 인증 수행 예시 — 인증 과정에서 성공할 때흐름도사용자가 username과 password를 제출하면 UsernamePasswordAuthenticationFilter는 HttpServletRequest 객체에서 username과 password를 추출하여 Authentication의 구현체인 UsernamePasswordAuthenticationToken을 생성합니다.다음으로 UsernamePasswordAuthenticationToken이 인증을 위해 AuthenticationManager 인스턴스로 전달됩니다.인증이 성공하면 아래 과정을 수행합니다.새로운 로그인이 발생한 것을 SessionAuthenticationStrategy에 통지합니다.SessionAuthenticationStrategy는 새로운 로그인이 발생할 때, 세션 관련 작업을 수행하는 전략입니다. 이전 세션을 무효화하거나, 동시 로그인 방지 정책을 적용할 수 있습니다.SecurityContextHolder를 새롭게 인증이 완료된 Authentication 데이터로 설정합니다.RememberMeServices.loginSuccess() 메소드가 호출됩니다. RememberMeService 를 설정하지 않은 경우, 어떤 작업도 수행되지 않습니다. 이 예제에서는 RememberMeService 기능을 별도로 설정하지 않았기 때문에 어떤 작업도 수행되지 않습니다.ApplicationEventPublisher가 InteractiveAuthenticationSuccessEvent를 발행합니다.AuthenticationSuccessHandler의 onAuthenticationSuccess() 메소드가 호출됩니다. 기본적으로 설정되어 있는 AuthenticationSuccessHandler 의 구현체는 SavedRequestAwareAuthenticationSuccessHandler 입니다. 로그인 페이지로 redirect되기 전, ExceptionTranslationFilter에서 RequestCache에 저장해둔 원래의 요청을 꺼내와 해당 요청으로 redirect 합니다.로그 확인위 로그는 흐름도 상 1 ~ 3번 과정에 대한 로그입니다.인증되지 않은 사용자가 POST http://localhost:8080/login 요청을 정상적인 인증 정보와 함께 전송 시, 발생하는 로그입니다. 로그를 통해 아래와 같은 사실을 알 수 있습니다.8번째 보안 필터로 UsernamePasswordAuthenticationFilter가 수행되고 ProviderManager 와 DaoAuthenticationProvider 가 순차적으로 수행되며 정상적으로 인증이 수행됩니다.인증 성공 시, 아래 작업을 수행합니다.CompositeSessionAuthenticationStrategy 에서 2가지 세션 관련 작업을 수행합니다.ChangeSessionIdAuthenticationStrategy 를 이용하여 Session Fixation 공격을 방지하기 위한 세션 ID 변경 작업을 수행합니다.CsrfAuthenticationStrategy 를 이용하여 세션에 연결된 CSRF 토큰을 교체합니다.SecurityContextHolder를 새롭게 인증이 완료된 Authentication 데이터로 설정합니다.SavedRequestAwareAuthenticationSuccessHandler 에 구현된 onAuthenticationSuccess() 메소드를 수행합니다. 인증 이전에 요청했던 리소스인 http://localhost:8080/private?continue URL로 redirect 를 수행합니다. Spring Security 내부 구현은 아래와 같습니다.기존 요청을 RequestCache에서 꺼내와서 해당 요청을 다시 수행하도록 redirect위 로그는 인증이 완료된 사용자가 GET http://localhost:8080/private로 redirect 될 때, 발생하는 로그입니다. 로그를 통해 아래와 같은 사실을 알 수 있습니다.15번째로 수행되는 보안 필터인 AuthorizationFilter 에서 사용자의 권한을 확인하는 과정을 수행합니다. 사용자의 권한이 모두 확인되면 FilterChainProxy는 모든 보안 필터가 수행되었으므로 보안적인 절차를 수행되었다는 로그를 출력합니다. 최종적으로 인증을 수행하기 전, 처음 요청했던 리소스에 접근할 수 있게 됩니다.글을 마치며Spring Security의 인증 과정은 체계적이고 확장 가능하게 설계되어 있습니다. 각 구성 요소는 명확한 책임을 가지고 있으며 이를 통해 다양한 인증 요구 사항을 유연하게 처리할 수 있습니다. 이번에는 기본적인 폼 기반 인증 과정을 설명했지만, Spring Security는 OAuth2, JWT, LDAP 등 다양한 인증 방식을 지원하므로 애플리케이션의 보안 요구 사항에 맞춰 적절한 인증 방식을 선택할 수도 있습니다.이 글이 Spring Security의 전반적인 인증 과정과 인증에 필요한 구성 요소에 대한 이해에 도움이 되기를 바랍니다.참고자료https://docs.spring.io/spring-security/reference/servlet/architecture.htmlhttps://docs.spring.io/spring-security/reference/servlet/authentication/architecture.htmlhttps://docs.spring.io/spring-security/reference/servlet/authentication/passwords/form.htmlhttps://docs.spring.io/spring-security/reference/servlet/authentication/passwords/dao-authentication-provider.htmlhttps://docs.spring.io/spring-security/reference/servlet/authentication/session-management.htmlSpring Security 의 인증 알아보기 was originally published in NAVER Pay Dev Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.

 # 들어가며 안녕하세요. NHN Cloud 서비스보안팀 지우중입...

생성형 AI 기술이 비즈니스 운영 방식을 재정의하는 가운데, IT 부서는 경쟁에서 뒤처지지 않기 위해 자신의 역할을 재평가해야 하는 시점에 와 있습니다. 이 글에서는 전문가들의 통찰력을 바탕으로 조직 내에 통합되는 데 있어 IT 부서가 어떤 도전과 기회에 직면했는지 살펴봅니다.

안녕하세요. 올리브영에서 프론트엔드 개발 업무를 담당하는 코난입니다. 올리브영 프론트엔드는 NEXT.JS 프레임워크를 사용하여 웹 페이지를 개발하고 있습니다. NEXT.JS 프레임워크를, Vercel이나, AWS Amplify…
COMMIT, MVCC 그리고 SET AUTOCOMMIT

안녕하세요. 상품 스쿼드의 백엔드 개발자 벙개맨⚡️ 입니다. 이번에 개선된 상품 설명 영역은 상품 상세 페이지 내에 위치하여 제품에 대한 자세한 설명이 포함된 문서를 말하는데요. 저희 올리브영에서는 상품 설명을 생성할 때 이미지 타입과 HTML…

이 글은 AI 서비스의 시장 동향, 기술 트렌드, 그리고 성공적인 도입 전략을 제시하며, AI 서비스가 비즈니스의 경쟁력 강화와 비용 절감에 어떻게 기여할 수 있는지를 분석합니다.

- 매주 배포하는 스타트업 환경에서 어떻게 일해야 효율적일까?- 헤이딜러에서 QA팀이 일하는 방식을 소개합니다.안녕하세요.피알앤디컴퍼니 QA Engineer 이동언입니다.헤이딜러는 고객용, 딜러용, 평가사용, 폐차, 딜러 콜, 평가사 콜 6가지 서비스를 운영하고 있습니다.PRND에서는 1주일 간격으로 배포를 진행하고 있는데요, 이러한 짧은 배포 주기를...

안녕하세요. FMS 엔지니어링팀의 렉스, 마르코입니다. 저희 팀은 쏘카 외 차량 관제를 위한 PoC(Proof of Concept)를 진행했으며, 그 일환으로 주행 이벤트 탐지 파이프라인을 구축했습니다. 이 과정에서 다양한 문제를 해결하며 파이프라인을 지속적으로 개선해왔습니다. 다음과 같은 분들이 읽으면 좋습니다. EDA(Event Driven Arc...

전통적인 관계형 데이터베이스의 한계를 뛰어넘어, 빅데이터 분석, 확장성, 유연성을 제공하는 NoSQL의 매력을 소개합니다. 빠른 속도와 다양한 데이터 모델로 현대 비즈니스의 요구를 충족시키는 NoSQL 솔루션들을 함께 알아보세요.

안녕하세요. 금융 FE 임문수 입니다.네이버페이 부동산에서 부하테스트를 진행한 경험을 바탕으로 부하테스트에 대한 설명과 nGinder를 활용한 부하테스트를 진행했던 과정에 대해 소개하려 합니다.배경네이버 부동산의 경우 레거시 시스템에서 새로운 프로젝트로의 전환을 진행하고 있습니다.최근 부동산에서 가장 접근이 많은 페이지 중 하나인 각 매물의 정보를 볼 수 있는 상세 페이지 전환을 진행하게 되면서,새로운 페이지가 기존의 사용자 요구를 원활하게 수용할 수 있는지 검증하고 많은 유저가 접근한 경우에도 서비스가 정상 작동을 확인하기 위해 부하테스트를 진행하게 되었습니다.네이버 페이 매물 상세 페이지부하테스트란?부하테스트(Load Testing)는 소프트웨어, 애플리케이션 또는 시스템이 실제 운영 환경에서 예상되는 부하 수준에서 어떻게 수행되는지 평가하는 과정으로 시스템이 사용자의 요구를 충족할 수 있는지, 그리고 특정 부하 조건에서도 안정적으로 작동하는지 확인합니다.서비스 출시 이전 부하테스트를 통해 시스템의 최대 처리 용량을 파악하고, 성능 개선이 필요한 부분을 식별할 수 있습니다.부하테스트 목적1. 성능 한계 확인사용자의 수요나 트래픽 증가 상황에도 안정적으로 서비스를 제공하기 위해 애플리케이션의 최대 처리 가능한 한계를 파악합니다.성능이 과도하게 떨어진다면 필요한 성능 개선 조치를 검토합니다.2. 서비스 코드와 서버 설정 검증서비스 코드에 문제는 없는지 메모리 누수가 발생하거나 응답시간이 과도하게 지연되지 않는지, 서버 설정에 문제가 없는지 확인합니다.3. 목표 TPS 달성을 위한 설정서비스의 목표 TPS 달성을 위한 필요한 서버 구성을 확인하고 예상치 못한 사용자 수요 증가나 트래픽 급증 상황에서도 안정적으로 운영될 수 있도록 준비합니다.이러한 목적을 달성하기 위해 아래 몇가지 점검 사항을 작성하여 부하테스트를 진행하였습니다.- 한 서버당 TPS는 얼마나 나오며 목표 TPS를 달성하기 위해서 필요한 서버 구성은 무엇인가?- 많은 사용자가 접근할 때 메모리 누수나 에러가 발생하지 않는가?- 현재 인프라 설정이 자원이 적거나 과도하게 많지는 않는가?- 하나의 서버당 가장 적합한 인스턴스 수는 몇 개인가?- HPA(HorizontalPodAutoscaler) 설정은 올바른 값을 바라보고 있고 부하 상황에 따라 확장, 축소 되는가?부하테스트 도구부하테스트를 진행하기 전에 아래에서 몇 가지 주요 부하테스트 도구들을 간략히 소개하고 넘어가겠습니다.ApacheBench (ab)ApacheBench는 Apache HTTP 서버 프로젝트에 포함된 경량의 명령줄 부하 테스트 도구입니다.간편한 사용법과 빠른 결과 제공이 장점이나, 고급 기능 부족과 분산 테스트의 한계로 인해 복잡한 테스트에는 제한적일 수 있습니다. 주로 간단한 웹 서버 성능 측정에 활용됩니다.JMeterApache JMeter는 다양한 서버 유형과 프로토콜에 대한 부하 테스트를 지원하며 오픈소스입니다. 사용자는 GUI 및 비GUI 모드를 통해 복잡한 테스트 시나리오를 쉽게 구현하고 실행할 수 있고 오랫동안 사용된 도구라 많은 예시와 커뮤니티 정보가 있습니다.그러나 대규모 테스트를 GUI 모드에서 실행시 리소스 사용이 많아 성능 저하가 발생할 수 있으며 기능이 많아 제대로 사용하려면 학습이 필요합니다.ArtilleryNode.js 기반의 부하테스트 도구로 HTTP/HTTPS, WebSocket, Socket.io 등 다양한 프로토콜과 애플리케이션에 대한 부하 테스트를 지원합니다.간단하게 npm으로 라이브러리를 설치하여 json 혹은 yaml 파일로 부하테스트를 진행할 수 있습니다. 상대적으로 적은 리소스로 높은 성능의 테스트를 실행할 수 있으며 실시간으로 모니터링 제공하고 테스트 결과를 저장하여 그래프로 상세 결과를 볼 수 있습니다.하지만 복잡한 테스트가 필요할 경우 기능이 제한적일 수 있으며 여러 테스트 결과를 비교 분석하기에 조금 불편 할 수 있습니다.nGrindernGrinder는 스크립트 기반 부하테스트 플랫폼으로, 테스트 관리를 위한 Controller와 부하 생성을 위한 Agent로 구성됩니다. 사용자 인터페이스로 테스트를 관리하며, 실시간 보고와 모니터링, 자동 결과 저장 기능을 제공합니다. 또한, 다중 지역 에이전트를 이용해 글로벌 부하 테스트 환경을 손쉽게 구축할 수 있습니다. 하지만, Jython 에 대한 지식이 없다면 복잡한 스크립트를 작성하기에 어려울 수 있고 분산 테스트를 위해 여러 Agent를 사용하는 경우 많은 양의 시스템 자원이 필요합니다.네이버의 경우 이미 사내에서 nGrinder 서비스가 구축되어 있어 테스트와 모니터링, 보고서를 쉽게 볼 수 있는 nGrinder를 이용하여 부하테스트를 진행하였습니다. (nGrinder 설치)테스트 실행 및 모니터링nGrinder 서비스는 부하테스트를 실행하는 성능 테스트 탭과 스크립트를 생성하는 스크립트 탭으로 나누어져 있습니다.부하테스트를 진행하기 위해 스크립트를 먼저 생성합니다.1. 스크립트 작성하기nGrinder는 Groovy와 Jython을 스크립트 언어로 지원합니다. 복잡한 테스트 시나리오 없이 단순 페이지 접근을 수행하는 경우, 샘플 코드를 적절히 수정하여 활용할 수 있습니다.네이버 부동산에서는 비 로그인 상황에서 충분히 테스트가 가능하여 기본으로 제공되는 비 로그인 기반의 샘플 코드를 수정하여 부하테스트를 진행했습니다.스크립트 명을 지정하고 생성하기를 누르면 기본 샘플 코드가 제공되며,단순 접근만 필요한 경우 샘플코드에서 Test 부분을 접근이 필요한 서비스 주소로 변경합니다.@Testpublic void test(){ // 서비스 주소 HTTPResponse response = request.GET("http://please_modify_this.com", params) if (response.statusCode == 301 || response.statusCode == 302) { grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", response.statusCode) } else { assertThat(response.statusCode, is(200)) }}다른 샘플 및 설명은 nGrinder GitHub 에서 확인할 수 있습니다.2. 부하테스트 진행스크립트를 작성했다면 성능 테스트 탭으로 이동하여 부하테스트를 진행합니다.각 에이전트에서 접근하는 사용자 수를 조절하여 서버에 부하를 줄 수 있습니다.우선 1회 실행하여 스크립트가 정상 동작하는지 확인합니다.스크립트가 정상 동작하는 지 확인한 이후 하나의 서버가 받을 수 있는 부하를 확인하기 위해 오토스케일링 옵션이 있다면 동작하지 않도록 설정하고 테스트를 진행합니다.참고 : 다른 서비스나 자원에 영향이 가지 않도록, 해당 서비스가 아닌 부분을 주석 처리하거나 정적 자원으로 변경 후 테스트 진행을 고려합니다.1. 웜업 진행테스트 사이에 코드 변경이나 장비 설정 변경으로 서버가 새로 올라간 경우,초기 서버의 불안정한 상태가 테스트 결과에 영향을 미치지 않도록 부하테스트 시작 전 1~5분 동안 서버 웜업을 진행합니다.nGrinder를 모니터링하여 TPS (Transactions Per Second) 그래프가 안정화될 때까지 웜업을 유지합니다.2. 테스트를 위한 최대 부하 찾기극한의 환경에서 테스트를 진행하기 위해, 서버가 재시작되거나 접근 불가 상태가 되지 않는 범위 내에서 최대 부하를 찾아냅니다.nGrinder의 Agent 수와 vuserPerAgent 값을 조절하여 에러가 발생하지 않는 범위 내에서 테스트를 진행하기 위한 최대 부하를 찾습니다.(참고: 총 vuser가 같더라도 agent가 많을 수록 에러가 더 잘 발생했습니다.)먼저 대략적인 총유저 수를 찾기 위해 유저를 변경하여 실행 종료하면서 nGrinder 모니터링 및 보고서에서 에러가 발생하지 않는 값을 찾습니다.에러가 많이 발생하거나 오른쪽 그래프가 너무 튄다면 총 유저 수를 줄이고에러가 발생하지 않으면서 오른쪽의 TPS 그래프가 너무 튀지 않는 대략적인 범위를 찾습니다.이후 부하 값을 자세히 확인하기 위해 서비스 서버 모니터링 도구를 함께 활용합니다.서버에서 가장 큰 병목 현상을 일으키는 자원을 확인하여 그 자원이 한계에 도달하는 최대 사용자 수를 결정합니다.부동산 FE 서버의 경우 CPU에 가장 많은 병목이 있었고, CPU 부하가 99%에 근접하게 유지되는 총 유저 값을 찾아 부하테스트를 진행했습니다.3. 설정 변경에 따른 데이터 수집인스턴스 개수, 서버 자원, 캐시 적용 등을 통해 결과 값을 기록하여 TPS 성능이 잘 나오면서 그래프가 안정된 형태를 유지하는 최적의 설정 값을 찾습니다.4. 기타 점검 사항 확인부하 테스트를 지속적으로 진행하면서 메모리 누수가 있는지 확인합니다. 이후 다른 점검이 끝났다면 오토스케일링이 발생 가능하도록 설정을 다시 변경하고 부하 상황에 따라 스케일 확장, 축소가 정상 동작 하는지 확인합니다.부하 테스트 결과점검 사항 확인한 서버당 TPS는 얼마나 나오며 목표 TPS를 달성하기 위해서 필요한 서버 구성은 무엇인가?서비스가 예상(혹은 측정) 부하를 견딜 수 있도록, 적절한 안전 마진을 고려하여 최대 RPS를 설정하였습니다. 서버의 실측 RPS 데이터(최대 부하)를 토대로 각 서비스의 목표 RPS에 맞게 대응 가능하도록 pod의 min, max 수치를 결정하였습니다.많은 사용자가 접근할 때 메모리 누수나 에러가 발생하지 않는가?단일 부하 테스트 수행 시 메모리 사용량이 지속적으로 상승하는지 확인하고 지속적으로 부하테스트를 진행하면서 사용량이 계속 상승하고, 가비지 컬렉션이 제대로 이루어지지 않는지 확인하였습니다.계속해서 메모리가 증가하여 재시작 되는 경우(출처: How we resolved a memory leak on our)테스트 진행 시 마다 메모리의 하단 지점과 메모리가 상승 하는 경우현재 인프라 설정이 자원이 적거나 과도하게 많지는 않는가?최대 부하상황에서의 서버 모니터링을 통해 자원을 결정하였습니다.nginx의 경우 최대 부하상황에서도 부하가 크지 않아 자원을 적게 할당했고, nextjs의 경우 부하 테스트 후 잔여 메모리가 높아 기존보다 메모리를 좀 더 높게 설정하였습니다.하나의 서버당 가장 적합한 인스턴스 수는 몇 개인가?부하 테스트를 통해 각 Pod 당 최적의 Instance 수를 도출하였습니다. 이전 부하테스트 경험을 통해 Instance 6~10개 범위 내에서 테스트를 진행하였고 nGrinder 모니터링 그래프의 평균 TPS와 TPS 그래프의 증감폭 (아래 박스 영역), 시스템의 안정성을 고려하여 인스턴스 수를 결정하였습니다.HPA(HorizontalPodAutoscaler) 설정은 올바른 값을 바라보고 있고 부하 상황에 따라 확장, 축소 되는가?최소값을 1로 주고 부하를 주어 바라보고 있는 값이 유의미한 값인지, 해당 값으로 오토스케일링이 잘 동작하는지 확인하였습니다.이외 부하테스트를 통해 확인 및 개선된 사항부하테스트를 진행하면서, 기존에 테스트한 다른 서비스에 비해 TPS가 낮게 나오는 문제가 발생했습니다. 서비스 모니터링 결과, BFF 서버에서 많은 부하로 인해 병목 현상이 발생하는 것을 확인하였고 이를 해결하기 위해 아래와 같은 사항들을 점검하여 일부를 개선했습니다.SSR 요청 개선해당 페이지의 경우 서버 사이드 렌더링(SSR)과정에서 순차적으로 처리되어야 하는 API 호출이 3단계로 나누어져 응답 시간 지연이 발생하고, SSR과정에서 너무 많은 API를 호출하고 있었습니다. SSR 에서 호출되는 API 호출 단계를 2단계로 줄여 next js 서버의 지연을 줄이고 상단 영역이 아닌 API들은 클라이언트에서 필요할 때 호출하도록 하여 페이지 접근과 동시에 발생하는 API 부하를 줄였습니다.캐시 적용해당 페이지의 경우 일부 데이터를 제외하고 사용자 기반이 아닌 매물에 대한 정보를 제공하여 실시간성이 중요한 페이지가 아니었기 때문에 `Cache-Control` 헤더를 설정하였습니다.s-maxage, max-age, stale-while-revalidate 값들을 적절히 지정하여 캐싱이 적용될 수 있도록 설정하였습니다.마치며지금까지 금융 FE에서 nGrinder를 사용하여 진행된 부하테스트를 소개해드렸습니다.처음에는 부하테스트와 관련된 용어와 개념들이 낯설고 복잡하게 느껴질 수 있으나,실제로 nGrinder를 사용해 부하테스트를 진행하면서, 명확한 목적과 방법을 갖고 진행하면 예상보다 접근하기 쉬웠다는 것을 깨닫게 되었습니다.네이버 페이 부동산에서는 이러한 방법으로 부하테스트를 진행하여 개선하였고 안정적으로 배포하여 현재 서비스를 제공하고 있습니다.이 글이 nGrinder를 활용한 부하테스트에 대한 이해를 돕고, 여러분의 서비스에 적용하여 보다 견고하고 안정적인 서비스를 제공하는 데 도움이 되기를 바랍니다.nGrinder를 활용한 부하테스트 was originally published in NAVER Pay Dev Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.

안녕하세요, ABC Studio에서 Demaecan(出前館, 이하 데마에칸) 앱을 개발하고 있는 김종식입니다. 데마에칸은 2000년부터 서비스를 시작한 일본 최대 규모의 음식 배달...

LINE에서는 3년 전 기존 헤드리스 CMS의 구조와 성능을 개선한 새로운 헤드리스 CMS, LandPress Content를 사내 임직원 대상으로 출시했습니다. 그 후 전통적인...

안녕하세요, 저는 올리브영의 데이터 플랫폼 인프라를 담당하는 슈로쿤입니다. 현재 저희 회사는 데이터 플랫폼 인프라의 안정적이고, 유연한 확장 그리고 보다 더 빠른 데이터 분석을 위해 Google Cloud…

최근 10년간 로봇공학은 놀라운 발전을 이루며, AI 기술과 결합한 지능형 로봇이 우리의 일상 속으로 성큼 들어왔습니다. 이 글에서는 인간과 로봇이 공존하는 미래를 준비하는 데 필요한 시사점을 제시합니다.

이 글에서는 로컬 LLM을 구축하고 활용하는 방법을 간략하게 안내합니다. 퍼블릭 LLM의 한계를 극복하고, 기업 맞춤형 AI 솔루션을 통해 데이터 보안을 강화하며, 독자적인 혁신을 이루고자 하는 분들에게 유용한 정보를 제공합니다.