4주차 발자국 | 인프런 워밍업 클럽 2기 - 백엔드
인프런 워밍업 클럽 2기
입문자를 위한 Spring Boot with Kotlin(https://inf.run/bXQQQ) 강의를 듣고 작성하였습니다
Spring Security
Spring Security는 Spring Boot 애플리케이션에 보안 기능을 손쉽게 통합할 수 있는 프레임워크입니다.
인증(Authentication)과 권한(Authorization) 관리 기능을 제공하여 애플리케이션을 보호하는 데 사용되며, OAuth2, JWT 같은 다양한 보안 프로토콜도 지원합니다.
dependencies {
implementation("org.springframework.boot:spring-boot-starter-security")
}@Configuration
class SecurityConfiguration {
@Bean
fun filterChain(httpSecurity: HttpSecurity): SecurityFilterChain? {
return httpSecurity
.authorizeHttpRequests { authorizeHttpRequests ->
authorizeHttpRequests
.requestMatchers(AntPathRequestMatcher("/**")).authenticated()
.anyRequest().permitAll()
}.csrf { csrf ->
csrf.disable()
}.headers { headers ->
headers.addHeaderWriter(XFrameOptionsHeaderWriter(XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
}.formLogin { formLogin ->
formLogin.defaultSuccessUrl("/")
}.logout { logout ->
logout.logoutRequestMatcher(AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
}.build()
}
}
/*
* NestJS에서는 strategy, Guard 를 사용하여 인증/인가처리를 구현합니다.
* strategy 에서는 JWT, oauth 등의 보안 프로토콜을 정의하고 적용할 수 있습니다.
* Guard를 정의하고 개별 request 위에 데코레이터로 적용할 수 있습니다 (global 적용도 가능)
*/
password encode
https://velog.io/@glencode/Spring-Security-Crypto를-사용한-비밀번호-암호화
시큐리티에 관한 강의를 아직 듣지 않았을 때, 패스워드 암호화를 해야하는 요구사항이 있어서 적용해보았던 내용을 기록합니다.
dependencies {
implementation("org.springframework.security:spring-security-crypto")
}@Configuration
class AuthConfig {
@Bean
fun passwordEncoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}
}의존성을 추가하고 config 를 정의합니다
@Service
class UserService(
private val userRepository: UserRepository,
private val passwordEncoder: PasswordEncoder,
// and so on..
) {...}/*
* dto.password는 사용자로부터 입력받은 password
* user.password는 DB에 encode 하여 저장된 password
*/
// encode
val encodedPassword = passwordEncoder.encode(dto.password)
// compare
if(!passwordEncoder.matches(dto.password, user.password)) {
throw BadRequestException("비밀번호가 틀렸습니다.")
}passwordEncoder 를 주입하고 위 매서드를 사용하여 활용할 수 있습니다.
/*
* nodejs 에서는 bcrypt를 사용하여 구현할 수 있습니다.
* const bcrypt = require('bcrypt');
*/
// encode
const encodedPassword = await bcrypt.hash(password, 10); // salt = 10
// compare
if (!(await bcrypt.compare(password, user.password))) {
throw new BadRequestException("비밀번호가 틀렸습니다.")
}
ControllerAdvice
@RestControllerAdvice는 @ControllerAdvice와 @ResponseBody의 기능을 결합한 어노테이션으로, REST API에서 예외를 처리할 때 주로 사용됩니다.
모든 컨트롤러에 대해 JSON이나 XML과 같은 형태로 일관된 응답을 반환할 수 있습니다.
@ControllerAdvice는 Spring MVC에서 예외 처리, 데이터 바인딩, 모델 객체의 변환 등을 전역적으로 관리할 수 있게 도와주는 어노테이션입니다.
서버 내부에서 입력 오류에 대한 경우를 전부 BadRequestException 으로 사용하고 message 를 다양하게 주고 있었는데, 실제로 에러가 발생했을 때 message가 오지 않아서 몹시 불편함을 느꼈습니다.
직접 입력한 메시지를 응답에 뿌려주기 위해 검색 후에 아래와 같은 GlobalExceptionHandler 를 구현하였습니다.
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(BadRequestException::class)
fun handleBadRequest(ex: BadRequestException): ResponseEntity<Map<String, String>> {
val errorResponse = mapOf(
"timestamp" to LocalDateTime.now().toString(),
"status" to HttpStatus.BAD_REQUEST.value().toString(),
"error" to "Bad Request",
"message" to (ex.message ?: "잘못된 요청입니다."),
)
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse)
}
}# 에러 응답 예시
{
"timestamp": "2024-10-23T04:22:59.959124",
"status": "400",
"error": "Bad Request",
"message": "먼저 입실해주세요"
}
/* NestJS ExceptionFilter
* 위와 동일한 작업을 하는 코드
*/
@Catch(BadRequestException)
export class GlobalExceptionFilter implements ExceptionFilter {
catch(exception: BadRequestException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const message = exception.getResponse() as string;
const errorResponse = {
timestamp: new Date().toISOString(),
status: HttpStatus.BAD_REQUEST,
error: 'Bad Request',
message: message || '잘못된 요청입니다.',
};
response.status(HttpStatus.BAD_REQUEST).json(errorResponse);
}
}
작은 회고
처음의 결심과 다르게, 강의 일정을 따라가는게 쉽지않았습니다. (게으르기 때문일까요~,,) 그래도 계속 하다보니 아주 조금...? 스프링에 대해 알아가는 느낌이 들어 정말 재밌었습니다. 꾸준히 노력해서, 프레임워크 상관없이 능숙하게 작업할 수 있는 백엔드 개발자가 되겠습니다.
프로젝트에 아직 @TODO 가 많은데, 시작한 프로젝트는 돌아오는 주까지 열심히 해서 잘 마무리하고 싶습니다. 학부생 때가 HTML의 마지막이라, 타임리프 작업이 가장 오래걸렸습니다... (사실 아직도 끝나지 않았습니다)
원래 쓰던 프레임워크와 비교해가며 이해하고 공부하는데, 이게 도움이 되었는지 아니었는지는 아직 잘 모르겠습니다. 아마 더 많이 사용해보는 시간이 필요할 거 같습니다. 공부법이든, 코딩 습관이든, 나만의 Best Practice 를 찾아가는 과정은 참 어려운거 같아요.
이번에 워밍업클럽을 통해 (!오랜만에!) 완강도 하고, 프로젝트도 하고, 강사님과 컨택할 수 있는 시간도 가지게 되어 정말 좋았습니다.
+ 온라인 세션에서 받은 이력서 피드백이 정말 많은 도움이 되었습니다. 이 자리를 빌려 감사의 마음을 전해드립니다 🙂
다음번에도 이런 기회가 온다면 다른 강의로 신청해보려고 합니다.
이 과정을 기획하고 관리하신 인프런 운영자님과, a-z까지 한 과정에 깔끔하게 담아내신 강사님 정말 고생많으셨습니다! 수료식에서 보아요 ( •͈૦•͈ )
댓글을 작성해보세요.