TIL 32 [ Spring 일정관리 앱 Lv2~5 ]

2025. 4. 4. 00:03·[내일배움캠프-Sparta]/Spring 6기
반응형

📣 Spring 일정 관리 앱 (JPA)

※ Dto, Entity가 있다고 작성.

✅ Lv 2 . 유저 CRUD

@Getter
@Entity
@Table(name = "schedule")
public class Schedule extends BaseEntity{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

    @ManyToOne // 연관관계
    @JoinColumn(name = "user_id")
    private Member member;

    @Column(columnDefinition = "longtext")
    private String contents;

    public Schedule(String title, String contents, Member member) {
        this.title = title;
        this.contents = contents;
        this.member = member;
    }

    public Schedule() {

    }

    public void update(String title, String contents) {
        this.title = title;
        this.contents = contents;
    }
}

일정은 유저명에 따라 작성되어야 하기 때문에 N:1 관계를 맺는다.  그래서 유저 필드명 대신 연관관계를 맺기위해 @ManyToOne, @JoinColumn 어노테이션을 활용해서 연관관계를 적용시키고, Lv1과 같이 유저만의 CRUD를 작성하였다.

✅ LV 3. 회원 가입

// 유저 생성
@PostMapping("/signup")  // 사실상 회원가입이 여기서 진행됨 ( ★ )
public ResponseEntity<MemberResponseDto> createMember(@RequestBody CreateMemberRequestDto requestDto) {

    MemberResponseDto responseDto = memberService.createMember(requestDto.getUserName(), requestDto.getEmail(), requestDto.getPassword());

    return new ResponseEntity<>(responseDto, HttpStatus.CREATED);
}

회원가입이라는 요구사항에 user 엔티티에 password 필드만 추가하라는 말만 있어서 필드만 추가하고 바로 넘어가서 4단계를 진행하였다. 그래서 뭐가 회원가입을 새로 만들어야 할 차에, 튜터님의 도움으로 회원가입이 이미 만들어져 있었던 것에 URI만 지정하여서 로그인 필터에 적용만 했었으면 되었다. ( 유저 생성 부분이 회원가입 )

 

의외로 간단하였다. 먼저 user 엔티티에 password 필드를 추가하고, 회원 가입이기 때문에 사실상 우리 컨트롤러에 이미 작업이 되어있다.

⭐⭐ 유저 CRUD 에 생성하는 곳이 바로 회원가입을 하는 곳이다.

✅ LV 4. 로그인

로그인 필터 + 빈 등록  ( 2주차 강의 실습 참고 )

@Slf4j
public class LoginFilter implements Filter {

    // URL 전부다 동일하게 경로를 작성해주어야 한다.
    private static final String[] WHITE_LIST = {"/","/api/members/signup","/api/members/login","/api/members/logout"};

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestURI = httpRequest.getRequestURI();

        HttpServletResponse httpResponse = (HttpServletResponse) response;

        log.info("로그인 필터 로직 실행");
        // WHITELIST에 포함되지 않은 경우
        if(!isWhiteList(requestURI)) {

            HttpSession session = httpRequest.getSession(false);

            if (session == null || session.getAttribute("memberEmail") == null) {
                throw new RuntimeException("로그인 해주세요.");
            }

            // 로그인 성공 로직
            log.info("로그인에 성공했습니다!");
        }

        // 다음 필터가 없으면 Servlet -> Controller, 다음 필터가 있으면 다음 필터로 감.
        chain.doFilter(request, response);
    }

    private boolean isWhiteList(String requestURI) {
        return PatternMatchUtils.simpleMatch(WHITE_LIST, requestURI);
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public FilterRegistrationBean logintFilter() {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new LoginFilter());
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.addUrlPatterns("/*"); // 전체 url이 모든요청 받도록

        return filterRegistrationBean;
    }
}

저번 2주차 숙련 Servlet Filter 강의 로그인 필터 실습을 참고하여, 클론코딩 후 로그인 필터를 적용하고 빈을 등록시키는 작업까지 한 후 컨트롤러에 로그인 로직을 추가하였다.

여기서 부터 막혔다. CRUD만 주구장창 해오다가 새로운 코드에 접근하려니까 너무 낯설었다. 튜터님에게 찾아가 로그인은 어떻게 구성을 하면 좋을까요?? 하다가 HttpSession을 활용해서 컨트롤러와 서비스단을 구현에 도움을 받았다.

Contorellor

// 로그인
@PostMapping("/login")
public ResponseEntity<String> login(
        @Valid @RequestBody LoginRequestDto requestDto,
        HttpSession httpSession
) {
    try {
        memberService.login(requestDto, httpSession);
        return new ResponseEntity<>("로그인 성공",HttpStatus.OK);
    } catch (IllegalArgumentException e) {
        return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED);
    }
}

// 로그아웃  ( 이 경우는 연결된 세션을 끊으면 되니까? invalidate )
@PostMapping("/logout")
public ResponseEntity<String> logout(HttpSession httpSession) {

    try {
        memberService.logout(httpSession);
        return new ResponseEntity<>("로그아웃 성공!",HttpStatus.OK);
    } catch (Exception e) {
        return new ResponseEntity<>(e.getMessage(),HttpStatus.UNAUTHORIZED);
    }
}

컨트롤러에서 /login, /logout은 WHITE_LIST에 포함되어 있기때문에 인증/인가 절차를 받지 않아 면제된다. 그래서 로그인 로그아웃 컨트롤러에 매핑하였고, HttpSession방식을 통해서 try/catch문으로 예외처리를 주었다. 세션이 없다면 401에러가 발생하는 식으로 구현하고, 로그인 성공시 "로그인 성공" 멘트가 발생된다.

Service

// 로그인 ( + 튜터님 도움 )
@Override
public void login(LoginRequestDto requestDto, HttpSession httpSession) {

    Member findMember = memberRepository.findByEmail(requestDto.getEmail()).orElseThrow();

    if (Objects.equals(requestDto.getPassword(), findMember.getPassword())) {
        httpSession.setAttribute("memberEmail", findMember.getEmail());
    } else {
        throw new RuntimeException("비밀번호가 다릅니다.");
    }
}

// 로그아웃
@Override
public void logout(HttpSession httpSession) {

    httpSession.invalidate(); // 세션 끊기
}

💥 로그인

먼저 가입이 되어있는 id값을 필요로 하기 때문에 그것을 가져와야한다.

이메일 요청한값과 DB에 저장된 이메일 값이 다르면 ElseThrow() 로 예외 처리 한다.

요구사항에는 Email, password 방식으로 로그인을 한다고 하였으니 id값의 이메일을 가져와야 한다. 

( 회원가입이 안되어있으면 회원가입부터 해야함. )

이후 비밀번호가 로그인을 시도한 요청한값과 DB에 저장된 비밀번호 서로 같을때 비로소 로그인 필터에 있는 Key("memberEmail")를 통해서 로그인이 성공한다.

비밀번호가 서로 다르면 "비밀번호가 다르다" 는 예외가 발생한다.

💥 로그아웃

이미 들어와있는 세션을 끊어주면 되기 때문에 비교적 간단하다. ( 현재 있는 세션 제거 ) invalidate()

 

⭐ 필독

  • 1. session.setAttribute(String , Object) : HttpSession에 세션 이름 및 회원 객체를 할당할 수 있음 
  • 2. session.getAttribute(String) : setAttribute로 지정했던 세션 이름에 접근하여 회원 정보 리턴

✅ Lv 5 . 다양한 예외처리 

다양한 예외처리의 경우 비교적 도전 과제 치고는 굉장히 쉬웠던것 같았다. ( 분량상 하나의 코드 분량만 다루겠다. )

@Getter
public class CreateMemberRequestDto {

    // 구글링하여 regexp 표현식 찾음.
    @Pattern(regexp = "^[a-zA-Z0-9]*$", message = "영어 알파벳과 숫자만 입력할 수 있습니다.")
    @Size(min = 4, message = "유저명은 최소 4글자 이상이어야 합니다.")
    @NotBlank(message = "유저명은 필수 입력 값입니다.")
    private final String userName;

    @Email
    private final String email;

    @Size(min = 4, message = "비밀번호는 최소 4글자 이상이어야 합니다.")
    private final String password;

    public CreateMemberRequestDto(String userName, String email, String password) {
        this.userName = userName;
        this.email = email;
        this.password = password;
    }
}

@Pattern 을 통해 정규 표현식을 지정하며, 실패시 "영어 알파벳 숫자만 입력할 수 있다."는 메세지를 설정해주었다.

@Size를 통해 최소입력 되어야 하는 길이를 지정해 min = 4 로 지정하였고, 실패시 기본 메세지는 "유저명은 최소 4글자 이상이어야 한다"는 명령어를 설정해주었다.

@NotBlank  는 null, 빈칸X 등 여러한 값을 잡기 때문에 설정하였으며, 요구적으로 userName은 필수 값이라는 의미이기 때문에 반드시 입력이 되어야 한다. 실패시, "유저명은 필수 입력 값입니다." 가 출력된다. 

 

이 때 실패 했다는 뜻은 해당되는 어노테이션의 조건이 맞지 않는 것에 따라 매칭되어서 출력된다.

// 유저 생성
@PostMapping("/signup")  // 사실상 회원가입이 여기서 진행됨 ( ★ )
public ResponseEntity<MemberResponseDto> createMember(@Valid @RequestBody CreateMemberRequestDto requestDto) {

    MemberResponseDto responseDto = memberService.createMember(requestDto.getUserName(), requestDto.getEmail(), requestDto.getPassword());

    return new ResponseEntity<>(responseDto, HttpStatus.CREATED);
}

유효성 검증이 필요한 곳에 @Valid 어노테이션을 매개변수에 설정해두어서 요청시 유효성 검증부터 진행하게 된다.

반응형

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

TIL 34 [ Spring 기초 프로젝트 ( Day 1 ) - 설계( ERD, API, 와이어프레임 ) ]  (0) 2025.04.07
TIL 33 [ 과제 끝 + 스탠다드반 ( 영속성 컨텍스트 ) ]  (0) 2025.04.04
TIL 31 [ Spring 일정관리 앱 Lv0 ~ 1, + 미니세션 ( 예외처리 ) ]  (0) 2025.04.02
TIL 30 [ Spring ( Data JPA ), 스탠다드반 ( Optional ) ]  (0) 2025.04.01
TIL 29 [ Spring ( Filter, 객체와 RDB, JPA , 영속성 컨텍스트, Entity 제작 ) ]  (0) 2025.03.31
'[내일배움캠프-Sparta]/Spring 6기' 카테고리의 다른 글
  • TIL 34 [ Spring 기초 프로젝트 ( Day 1 ) - 설계( ERD, API, 와이어프레임 ) ]
  • TIL 33 [ 과제 끝 + 스탠다드반 ( 영속성 컨텍스트 ) ]
  • TIL 31 [ Spring 일정관리 앱 Lv0 ~ 1, + 미니세션 ( 예외처리 ) ]
  • TIL 30 [ Spring ( Data JPA ), 스탠다드반 ( Optional ) ]
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)
  • 블로그 메뉴

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

  • 공지사항

  • 인기 글

  • 태그

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

티스토리툴바