TIL 28 [ Spring ( Bean Validation, Cookie ) ]

2025. 3. 28. 22:26·[내일배움캠프-Sparta]/Spring 6기
반응형

1️⃣Bean Validation

✅검증 ( Validation )

📣 Controller의 주요 역할중 하나는 Validation 이다. HTTP 요청이 정상인지 검증한다.

특정 데이터의 값이 유효한지 확인하는 단계를 의미

  • 시스템이 미리 정한 사양에 부합하고 있는지 검증하는 것

🟡 역할

  1. 검증을 통해 적절한 메세지를 유저에게 보여줘야 한다.
  2. 검증 오류로 인해 정상적인 동작을 하지 못하는 경우는 없어야 한다.
  3. 사용자가 입력한 데이터는 유지된 상태여야 한다.

⏩ 실제 서버를 운영하다보면 기능의도와 다른 다양한 사용법을 보게 됨

      예시 ) Enter로 입력 완료, 누구는 click, Tab + Enter

🟡 검증의 종류

  1. 프론트 검증
    • 해당 검증은 유저가 조작할 수 있음으로 보안에 취약하다.
    • 보안에 취약하지만 꼭 필요함
      • ex) 비밀번호에 특수문자가 포함되어야 한다면 즉각적인 alert 가능 → 유저 사용성 ⬆
  2. 서버 검증
    • 프론트 검증없이 서버에서만 검증 한다면 유저 사용성  ⬇
    • API Spec을 정의해서 Validation 오류를 Response 예시에 남겨줘야 한다.
    • 서버 검증은 필수 ❕❗
  3. DB 검증
    • Not Null, Default 같은 제약조건을 설정
    • 최종 방어선 역할

⚡ 기본적으로 프론트, 서버, DB 모두 검증을 꼼꼼하는것이 바람직하다 ❗❗❗❗

 

🟢 BindingResult

Spring에서 기본저긍로 제공되는 Validation 오류를 보관하는 객체 

  • Errors 인터페이스를 상속받은 인터페이스이다.
  • Errors 인터페이스는 에러의 저장과 조회 기능을 제공한다.
  • BindingResult는 addError() 와 같은 추가적인 기능을 제공한다.
  • Spring이 기본적으로 사용하는 구현체는 BeanPropertyBindingResult 이다.

파라미터에 BindingResult가 있는 경우

⛔ 주의사항 : BindingResult 파라미터는 검증대상 파라미터 뒤에 위치해야만 한다.

  • @ModelAttribute 필드 or 객체에 파라미터 바인딩 오류가 발생
    • BindingResult에 오류가 보관되고 Controller는 정상적으로 호출된다.

@ModelAttribute는 파라미터를 필드 하나하나에 바인딩한다. 파라미터에 Binding Result가 함께 있는 경우 만약 그중 하나의 필드에 오류가 발생하면 해당 필드를 제외하고 나머지 필드들만 바인딩 된 후 Controller가 호출된다.

✅Bean Validation

  • 객체의 필드나 메서드에 제약 조건을 설정하여, 올바른 값을 가지고 있는지 검증하는 표준화된 방법
  1. Bean Validation은 기술 표준 인터페이스이다.
  2. 다양한 Annotation들과 여러가지 Interface로 구성되어 있다.
    • Bean Validation(인터페이스) 구현체인 Hibernate Validator를 사용한다.
@RestController
public class BeanValidationRestController {

    @PostMapping("/request-body")
    public String beanValidationV2(
            @Validated @RequestBody SignUpRequestDto dto
    ) {
        // 로직
        
        // 문자 Data 반환
        return "회원가입 완료";
    }

}
  • Annotation을 적용시키는것 만으로 Validation을 아주 쉽게 적용할 수 있다.
  • Controller에 개발자가 기본적인 검증 로직을 작성할 필요가 없어졌다.
  • RestController의 @RequestBody에도 사용할 수 있다.

@NotBlank ( 최상위 )

  1. null을 허용하지 않는다.
  2. 공백(” “)을 허용하지 않는다. 하나 이상의 문자를 포함해야한다.
  3. 빈값(””)을 허용하지 않는다.
  4. CharSequence 타입 허용
    • String은 CharSequence(Interface)의 구현체이다.

@NotNull ( 최하위 )

  1. null을 허용하지 않는다.
  2. 모든 타입을 허용한다.

@NotEmpty

  1. null을 허용하지 않는다.
  2. 빈값(””)을 허용하지 않는다.
  3. CharSequence, Collection, Map, Array 허용

@Valid, @Validated 차이점

  1. @Valid 는 JAVA 표준이고 @Validated 는 Spring 에서 제공하는 Annotation이다.
  2. @Validated 를 통해 Group Validation 혹은 Controller 이외 계층에서 Validation이 가능하다.

Validator 적용

  • Validator 적용 전
    • @ModelAttribute 각각의 필드 타입에 맞추어 바인딩(변환) 시도
      • 성공 : Controller 정상 호출
      • 실패 : TypeMismatch FieldError 발생
  • Validator 적용 후
    • @ModelAttribute → 각 필드 바인딩 → 성공한 필드만 Bean Validation 적용
      • Integer 타입 필드에 문자가 오면 애초에 검증의 의미가 없다.
      • 성공 : String 필드에 문자입력 → 바인딩 성공 → String 필드에 Bean Validation 적용
      • 실패 : Integer 필드에 문자입력 → 바인딩 실패 → bindingResult에 TypeMismatch FieldError 추가 → 바인딩에 실패한 필드는 값이 없음(null) → Bean Validation 적용하지 않음

✅ 에러 메세지

Spring의 Bean Validation은 Default로 제공하는 Message들이 존재하고 임의로 수정할 수 있다.

NotNull.Object.fieldName

  • Annotation의 message 속성 사용
@Data
public class TestDto {

    @NotBlank(message = "메세지 수정 가능") // 에러 메세지 변경 ("메세지 수정 가능")
    private String stringField;

}

✅ Bean Validation 충돌 ( DTO 분리 )

💡 등록, 수정 API에서 각각 다른 Validation이 적용된다면?

  • 상품
    • id (식별자)
    • name (이름)
    • price (가격)
    • count (재고)
  • 상품 등록 API
    • 식별자 값은 필수가 아니다.
    • name은 null, “”, “ “을 허용하지 않는다.
    • price는 10 ~ 10000 사이의 숫자로 생성한다.
    • count는 1 ~ 999 사이의 숫자로 생성한다.
  • 상품 수정 API
    • 식별자 값이 필수이다.
    • name은 null, “”, “ “을 허용하지 않는다.
    • price는 무제한으로 허용한다.
    • count는 1 ~ 999 사이의 숫자로 생성한다.
      • 실무에서는 등록 폼과 수정 폼 자체를 분리해서 사용하기 때문에 DTO 분리 방법을 사용하면 된다.
      • 단, 네이밍은 일관성있게 작성해야 한다.(SaveRequestDto, UpdateRequestDto)
  • DTO 분리
    • 실제로 간단한 프로젝트를 개발해보면 저장, 수정시 Request가 비슷한 경우가 있다.
    • 각각의 장단점이 존재하지만 어설프게 하나로 합칠 경우 유지보수시 엄청난 경험을 할 수 있다.
    • RequestDto가 변한다는건 해당 API의 스펙 자체가 변경되어 많은 수정이 발생한다.
    • 실무에서는 거의 발생하지 않는 경우기 때문에 간단한게 아니라면 대부분 분리하도록 하자!
  • @Validated VS @Valid
    1. @Validated
      • 속성값이 존재한다.
      • spring이 제공하는 Annotation
    2. @Valid
      • 속성값이 존재하지 않는다, groups 기능 지원하지 않는다.
      • groups 기능을 사용하려면 @Validated를 사용해야 한다.
      • Java 표준 Annotation
        • @Validated VS @Valid
          1. @Validated
            • 속성값이 존재한다.
            • spring이 제공하는 Annotation
          2. @Valid
            • 속성값이 존재하지 않는다, groups 기능 지원하지 않는다.
            • groups 기능을 사용하려면 @Validated를 사용해야 한다.
            • Java 표준 Annotation

✅ @ModelAttribute, @RequestBody 

@ModelAttribute와 @RequestBody 차이점

  1. @ModelAttribute
    • 각각의 필드 단위로 바인딩한다.
    • 특정 필드 바인딩이 실패하여도 나머지 필드는 정상적으로 검증 처리할 수 있다.
    • 특정필드 변환 실패
      • 컨트롤러 호출, 나머지 필드 Validation 적용
  2. @RequestBody
    • 필드별로 적용되는것이 아니라 객체 단위로 적용된다.
    • MessageConverter가 정상적으로 동작하여 Object로 변환하여야 Validation이 동작한다.
    • 특정필드 변환 실패
      • 컨트롤러 미호출, Validation 미적용
  • 추가내용
    • bindingResult.getAllErrors()는 FieldError와 ObjectError 모두 반환한다.
    • Spring은 MessageConverter를 이용해 Error 객체들을 변환하여 응답한다.
    • RequestDTO 의 경우, 생성, 수정, 삭제, 모두 비슷하게 생겼어도 따로 분리해서 사용하자.
    • 작성한 코드는 예시일 뿐 실제로는 API Spec에 맞는 응답을 만들어 클라이언트에 전달 해야한다.
      • @ControllerAdvice

2️⃣ Cookie

✅ Cookie

사용자의 웹 브라우저에 저장되는 정보로 사용자의 상태 or 세션을 유지하거나 UX을 개선하기 위해 사용

💥 F12 ( 개발자 도구 ) ➡ Application ➡ 쿠키 ➡ 해당하는 웹URL ➡ (로그인시 쿠키 정보 확인 가능)

 

✅ Cookie Header

📚 서버에서는 HTTP 응답 헤더에 Set-Cookie 속성을 사용해 생성하고 설정할 수 있다.

  • Cookie Header
    1. Set-Cookie
      • Server에서 Client로 Cookie 전달(Response Header)
    2. Cookie
      • Client가 Cookie를 저장하고 HTTP 요청시 Server로 전달(Request Header)
  • Response 알아보기
    • Cookie의 생명주기
      1. 세션 Cookie
        • 만료 날짜를 생략하면 브라우저 완전 종료시 까지만 유지된다.(Default)
          • expires, max-age 가 생략된 경우
        • 브라우저를 완전 종료 후 다시 페이지를 방문했을 때 다시 로그인을 해야한다.
      2. 영속 Cookie
        • 만료 날짜를 입력하면 해당 날짜까지 유지한다.
          • expires=Sat, 11-Dec-2024 00:00:00 GMT;
            • 해당 만료일이 도래하면 쿠키가 삭제된다.
          • max-age=3600 (second, 3600초는 한시간. 60 * 60)
            • 0이 되거나 음수를 지정하면 쿠키가 삭제된다.
    • Cookie의 도메인
      • 쿠키가 아무 사이트에서나 생기고 동작하면 안된다!
        • 필요없는 값 전송, 트래픽 문제 등이 발생한다.
      • domain=spartacodingclub.kr
        • domain=spartacodingclub.kr를 지정하여 쿠키를 저장한다.
        • dev.spartacodingclub.kr와 같은 서브 도메인에서도 쿠키에 접근한다.
      • domain을 생략하면 현재 문서 기준 도메인만 적용한다.
    • Cookie의 경로
      • 1차적으로 도메인으로 필터링 후 Path가 적용된다.
      • 일반적으로 path=/ 루트(전체)로 지정한다.
      • 위 경로를 포함한 하위 경로 페이지만 쿠키에 접근한다.
        • path=/api 지정
          • path=/api/example 가능
          • path=/example 불가능
    • Cookie 보안
      1. Secure
        • 기본적으로 Cookie는 http, https 구분하지 않고 전송한다.
        • Secure를 적용하면 https인 경우에만 전송한다. s = Secure
      2. HttpOnly
        • XSS(Cross-site Scripting) 공격을 방지한다.
          • 악성 스크립트를 웹 페이지에 삽입하여 다른 사용자의 브라우저에서 실행되도록 하는 공격
        • 자바스크립트에서 Cookie 접근을 못하도록 막는다.
        • HTTP 요청시 사용한다.
      3. SameSite
        • 비교적 최신 기능이라 브라우저 지원여부를 확인 해야한다.
        • CSRF(Cross-Site Request Forgery) 공격을 방지한다.
          • 사용자가 의도하지 않은 상태에서 특정 요청을 서버에 전송하게 하여 사용자 계정에서 원치 않는 행동을 하게 만든다.
        • 요청 도메인과 쿠키에 설정된 도메인이 같은 경우만 쿠키 전송

✅ 로그인 상태 유지

Cookie는 주로 사용자 세션 관리(로그인, 장바구니, 접속시간)나 광고 트래킹(사용자 행동) 등의 목적으로 사용됨.

 

  • Cookie 사용법
    • 한번 로그인에 성공하면 HTTP Response에 쿠키를 담아서 브라우저에 전달한다.
    • 브라우저는 요청마다 Cookie를 함께 전송한다.
    • 보안상의 문제로 name=원욱이 아닌 userId=1과 같은 index 정보를 저장한다.
      • 이것 또한 보안문제가 있다.
    • 요구사항에 맞추어 세션 Cookie를 사용할지 영속 Cookie를 사용할지 결정한다.

✅ 문제점

Cookie는 보안에 취약하여 userId=1 형태의 방식으로 로그인을 구현하지 않는다.

쿠키 값은 임의로 변경할 수 있다.  ( 치명적 💥 )

  • Client가 임의로 쿠키의 값을 변경하면 서버는 다른 유저로 인식한다.
  • userId = 임의로 수정
    • 브라우저 개발자도구(F12) → Application → Cookies → 값 수정 가능
    • 실제로는 암호화되어 저장되어있는 Value들을 볼 수 있다!

Cookie에 저장된 Data는 탈취되기 쉽다.

  • userId = 주민번호, userId = 인덱스 값
  • 쿠키는 네트워크 전송 구간에서 탈취될 확률이 매우 높다.
    • HTTPS를 사용하는 이유 중 하나에 속한다.
    • 민감한 정보를 저장하면 안된다.
  • 한번 탈취된 정보는 변경이 없다면 반영구적으로 사용할 수 있다.
  • 보안 대처방법
    1. 쿠키에 중요한값을 저장 ❌
    2. 알아보지 못하는 값을 노출 ( 암호화 )
      • 일반적으로 암호화된 Token을 쿠키에 저장한다.
      • 서버에서 암호화된 Token과 사용자를 매핑해서 인식한다.
      • Token은 서버에서 관리한다.
    3. 토큰은 해커가 임의의 값을 넣어도 동작하지 않도록 만들어야 한다.
    4. 해커가 토큰을 탈취해도 사용할 수 없도록 토큰 만료시간을 짧게 설정한다.
    5. 탈취가 의심되는 경우 해당 토큰을 강제로 만료시키면 된다.
      • 접속기기 혹은 IP가 다른 경우 등
반응형

'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글

TIL 29 [ Spring ( Filter, 객체와 RDB, JPA , 영속성 컨텍스트, Entity 제작 ) ]  (0) 2025.03.31
Spring ( Session, Token, JWT )  (0) 2025.03.30
TIL 27 [ Spring ( SOLID 객체지향, Container와 Bean, 싱글톤 ) + 스탠다드반 (DI / IOC) ]  (0) 2025.03.27
TIL 26 [ Spring 일정관리 앱 과제 해설 + 학습법 & 동기부여 세션 ]  (0) 2025.03.26
TIL 25 [ Spring 일정 관리 앱 과제 ( 필수 . . . JDBC..??) + 미니세션 ( API, ERD ) ]  (0) 2025.03.24
'[내일배움캠프-Sparta]/Spring 6기' 카테고리의 다른 글
  • TIL 29 [ Spring ( Filter, 객체와 RDB, JPA , 영속성 컨텍스트, Entity 제작 ) ]
  • Spring ( Session, Token, JWT )
  • TIL 27 [ Spring ( SOLID 객체지향, Container와 Bean, 싱글톤 ) + 스탠다드반 (DI / IOC) ]
  • TIL 26 [ Spring 일정관리 앱 과제 해설 + 학습법 & 동기부여 세션 ]
dimenshun
dimenshun
한 소년의 개발 일기
    반응형
  • dimenshun
    Dev Life Notes
    dimenshun
  • 전체
    오늘
    어제
    • 분류 전체보기 (268)
      • CS (23)
        • 자료구조 (0)
        • 알고리즘 (0)
        • 컴퓨터 구조 (8)
        • 네트워크 (6)
        • 운영체제 (3)
        • DB ( + SQLD ) (5)
        • SW공학 (1)
      • 프로그래밍 (3)
        • Java (0)
        • Spring (0)
        • HTML,CSS (3)
        • JavaScript (0)
      • 개발 툴 (7)
        • Git(버전관리) (1)
        • Docker (3)
        • AWS (2)
        • JSP (1)
      • 코딩테스트(Algorithm) (125)
        • 백준 (6)
        • 프로그래머스 (119)
      • [내일배움캠프-Sparta] (110)
        • Spring 6기 (106)
        • KPT 회고 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    AWS
    컴퓨터구조
    Python
    cs
    It
    SQL
    메모리
    네트워크
    web
    spring
    세션
    Testcode
    내일배움캠프
    network
    배포
    OS
    CPU
    개발자
    Til
    docker
    코딩테스트
    트랜잭션
    백엔드
    운영체제
    db
    KPT
    웹
    Java
    알고리즘
    SQLD
  • hELLO· Designed By정상우.v4.10.3
dimenshun
TIL 28 [ Spring ( Bean Validation, Cookie ) ]
상단으로

티스토리툴바