728x90

(1) WIL(Weekly I Learned) - 9~10주차

  • 4/23일 드디어 기다리던 마지막 주차인 최종 프로젝트가 시작되었습니다.
  • 팀은 총 7인 백엔드(Spring) 3분(저 포함), 프론트엔드(React) 3분으로 구성되었으며 최종프로젝트 인 만큼 디자이너 1분도 포함되어 프로젝트를 진행하였습니다.

(2) 일정

  • 4/25 - S.A 제출
  • 4/26 12:00 ~ 17:00 - 팀 별 시작 면담
  • 4/30 10:00 ~ 15:00 - 팀 별 1차 면담
  • 5/5 19:00 ~ 20:00 - 미소 협력사 발표
  • 5/8 17:30 ~ 18:30 - 미니프로젝트 코드기반 코드리뷰 (이태훈 튜터님)
  • 5/13 자정까지 - 팀 별 중간 결과물 제출
  • 5/27 자정까지 - 팀 별 최종 결과물 제출

(3) 배운점

  • WebDriver를 이용한 크롤링
  • JPA와 Pageable을 이용한 페이징 처리
  • nativeQuery
  • JPQL
  • Junit5 TestCode 작성
  • @Scheduler

(4) 최종 프로젝트


(5) 느낀점

  • 최근 항해99에서 만난 사람들과 오프라인에서 만났다. 다들 시간이 없어 일요일에 약속을 잡아 오랜만에 회고록을 쓰게 되었습니다... 반성하는 시간이 되네요.
  • 최종 프로젝트는 미니 프로젝트가 끝나기 전에 얘기를 맞췄던 분들과 팀이 구성이 되었고 거기에 더해 프론트 한분, 백엔드 한분이 추가되어 팀이 결정 되었습니다.(디자이너분은 팀 결정과 함께 스파르타에서 연결 시켜주셨습니다.)
  • 최종프로젝트가 진행되면서 이전에 신경쓰지 않았던 보안부분에 대해 http -> https로 변경하는 등 많은 신경을 쓰게 되었습니다. 또한 무중단 배포를 통하여 서비스가 끊기지 않고 업데이트를 할 수 있도록 설계를 하였습니다. 어려운 기능보다는 사용자 경험, 완벽한 기능을 만드는 것에 초점을 두고 만들어 이전 프로젝트에 비해 적은 스코프를 가지고 만들었지만 시간은 더 들었습니다.
  • 다음주가 되면 실제 서비스를 배포할 가능성이 높아 많은 기대를 하고있습니다.

반응형
728x90

(1) WIL(Weekly I Learned) - 7~8주차

  • 4/9일 항해를 시작한 후 처음으로 프로젝트 진행 기간이 2주인 미니 프로젝트 주차가 시작되었습니다.
  • 팀은 총 4인 백엔드(Spring) 2분(저 포함), 프론트엔드(React) 2분으로 구성되었으며 다시 팀장을 맡게되었습니다.
  • 이전 클론코딩에서의 부족함을 느낀분들을 위해 캐치업 조가 새로 생겨 프로젝트 조, 캐치업 조를 본인이 선택 하여 들어갈 수 있도록 변화가 생겼습니다.

(2) 일정

  • 4/9 - S.A 제출 / 주제 선정 / 와이어프레임 / API설계 / 중간점검 목표 / 2주 진행 계획
  • 4/10 - 튜터 서면 피드백
  • 4/14 - Deer, 알파카 협력사 발표
  • 4/16 - 팀 별 중간체크 / 발제 없음1) WIL(Weekly I Learned) - 7주차
  • 4/22 자정까지 - 결과물 제출
  • 4/23 10:00 ~ 11:00 - 둘러보기
  • 4/23 11:00 - 실전프로젝트 발제

(3) 배운점

  • Web Socket 통신을 이용한 채팅
  • Embedded Redis 사용
  • Spring Security 와 JWT 혼합 사용
  • HtmlEmail객체를 이용한 Email 보내기
    • 비밀번호 찾기를 위해 사용
  • 카카오 소셜로그인
    • 프론트엔드에서 처리 후 카카오에서 받은 Token을 백엔드로 넘겨주어 해당 유저 정보를 DB에 저장 및 JWT 생성 후 프론트엔드로 새로 생성된 Token을 발급

(4) 미니 프로젝트


(5) 느낀점

  • 다들 자신의 주특기에 자신있는 분들을 만나 프로젝트를 순조롭게 진행 할 수 있어 4/16 중간체크 이전 까지 주요 기능인 채팅을 완성할 수 있었습니다.
  • 지금까지 정확한 시간계획 없이 프로젝트 진행, 주특기 공부 등을 하고 있어 정리가 잘 안되는 느낌을 받아 항해 99에서 만난 분들과 시간을 정해 알고리즘 공부를 하기로 하였습니다. 21:30 ~ 23:30 까지 Python 을 이용하여 프로그래머스, 백준 알고리즘 을 풀어 서로 도움되는 시간을 가질 수 있고, 지속적으로 알고리즘을 공부함으로 알고리즘에 익숙해 질 수 있을 것 같습니다.
  • 채팅을 만들며 기본 CRUD를 사용하는 것이 아닌 WebSocket통신을 접할 수 있어 알림, 채팅 등 실시간으로 이루어지는 것에 대해 조금이나마 이해할 수 있었습니다. 아직 Redis에 대해서는 더 공부해야 할 필요성을 느꼇습니다.
  • 처음 WebSocket을 이용하여 채팅을 구현한 것이여서 기분이 매우 좋았으며, 팀원분들과 직접 구현한 채팅 페이지에서 채팅을 하며 소소한 즐거움을 얻을 수 있었습니다.
  • 카카오 소셜로그인은 심화 Spring강의에서 한 프로젝트 안에 백엔드, 프론트엔드가 공존할때에만 구현한 경험이 있어 이를 토대로 소셜로그인을 React와 같이 하려 했지만 Redirect하는 부분에서 Token을 어떻게 넘겨줘야하는지에 문제가 있어 프론트엔드에서 카카오Token을 백엔드로 넘겨주는 방식으로 해결하였습니다. 이게 올바른 방법인지는 아직 해답을 찾이 못하였으나 마지막 코드리뷰때 문의할 예정입니다.
  • 비밀번호 변경하는 방법을 메일보내는 것을 통해 구현하였습니다. 비밀번호 찾기 시 랜덤한 인증번호를 메일과 프론트엔드에 보내어 인증번호를 올바르게 입력하였을때 비밀번호를 변경 할 수 있는 페이지에 나오게 하였는데, 그리 어려운 기능은 아니였지만 Spring boot 코드에서 메일을 보내는 기능을 추가 하여 재밌는 경험이였습니다.

반응형
728x90

(1) 유효성 검사(Validation) 란?

  • 어떤 데이터의 값이 유효한지, 타당한지 확인하는 것을 의미합니다.
  • 보안적인 측면에서 올바르지 않는 데이터가 서버로 전송되거나 DB에 전송되지 않도록 하는 것입니다.

(2) 관련 어노테이션

  • JSR-303 Validator
    • @AssertFalse - 거짓인가?
    • @AssertTrue - 참인가?
    • @Max - 지정 값 이하인가?
    • @Min - 지정 값 이상인가?
    • @DecimalMax - 지정 값 이하 실수인가?
    • @DecimalMin - 지정 값 이상 실수인가?
    • @NotNull - Null이 아닌가?
    • @Null - Null 인가?
    • @Digits (integer=, fraction=) - 대상 수가 지정된 정수, 소수 자리 수 이내인가?
    • @Pattern(regex=,flag=) - 정규식을 만족 하는가?
    • @Future - 미래날짜인가?
    • @Past - 과거 날짜인가?
    • @Size(min=, max=) - 문자열, 배열 등의 크기가 지정크기를 만족 하는가?
  • Hibernate
    • @NotEmpty - Empty 값이 아닌가?
    • @Email - 이메일 형식인가?
    • @URL - URL 형식인가?
    • @Length(min=, max=) - 문자열의 길이가 min과 max 사이인가?
    • @Range(min=, max=) - 숫자 범위가 min과 max 사이인가?

(3) 사용법

  • build.gradle
dependencies {
	compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    
    // https://mvnrepository.com/artifact/org.json/json
    implementation group: 'org.json', name: 'json', version: '20160810'
    
	implementation 'org.springframework.boot:spring-boot-starter-validation:2.3.3.RELEASE'
}
  • DTO
    @NotNull
    @NotBlank(message = "이메일 입력은 필수입니다.")
    @Email(message = "이메일 형식으로 입력해 주세요.")
    private String email;

    @NotNull
    @NotBlank(message = "비밀번호 입력은 필수입니다.")
    private String password;
  • Controller
    //회원 가입
    @PostMapping("/api/user/signup")
    public Object registerUsers(@Valid @RequestBody UserSignupRequestDto userSignupRequestDto){

    }
  • ErrorCode
import lombok.Getter;

public enum ErrorCode {

    NOT_NULL("ERROR_CODE_NOT_NULL","필수값이 누락되었습니다.")
    , MIN_VALUE("ERROR_CODE_MIN_VALUE", "최소값보다 커야 합니다.")
    , PATTERN("ERROR_CODE_PATTERN","값 형식이 다릅니다.")
    , NOT_BLANK("ERROR_CODE_NOT_BLANK","필수값이 누락되었습니다.")
    , EMAIL("ERROR_CODE_EMAIL","이메일 형식이 아닙니다.")
    ;

    @Getter
    private String code;

    @Getter
    private String description;

    ErrorCode(String code, String description) {
        this.code = code;
        this.description = description;
    }
}
  • ErrorResponse
import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class ErrorResponse {

    private String code;

    private String description;

    private String errorMessage;

    public ErrorResponse(String code, String description) {
        this.code = code;
        this.description = description;
    }

    public ErrorResponse(String code, String description, String errorMessage) {
        this.code = code;
        this.description = description;
        this.errorMessage = errorMessage;
    }
}
  • ExceptionController
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

@Slf4j
@ControllerAdvice
public class ExceptionController {

    /**
     * @valid  유효성체크에 통과하지 못하면  MethodArgumentNotValidException 이 발생한다.
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> methodValidException(MethodArgumentNotValidException e, HttpServletRequest request){
        log.warn("MethodArgumentNotValidException 발생!!! url:{}, trace:{}",request.getRequestURI(), e.getStackTrace());
        ErrorResponse errorResponse = makeErrorResponse(e.getBindingResult());
        return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.BAD_REQUEST);
    }

    private ErrorResponse makeErrorResponse(BindingResult bindingResult){
        String code = "";
        String description = "";
        String errorMessage = "";

        //에러가 있다면
        if(bindingResult.hasErrors()){
            //DTO에 설정한 meaasge값을 가져온다
            errorMessage = bindingResult.getFieldError().getDefaultMessage();

            //DTO에 유효성체크를 걸어놓은 어노테이션명을 가져온다.
            String bindResultCode = bindingResult.getFieldError().getCode();

//            switch (bindResultCode){
            switch (Objects.requireNonNull(bindResultCode)){
                case "NotNull":
                    code = ErrorCode.NOT_NULL.getCode();
                    description = ErrorCode.NOT_NULL.getDescription();
                    break;
                case "NotBlank":
                    code = ErrorCode.NOT_BLANK.getCode();
                    description = ErrorCode.NOT_BLANK.getDescription();
                    break;
                case "Min":
                    code = ErrorCode.MIN_VALUE.getCode();
                    description = ErrorCode.MIN_VALUE.getDescription();
                    break;
                case "Pattern":
                    code = ErrorCode.PATTERN.getCode();
                    description = ErrorCode.PATTERN.getDescription();
                    break;
                case "Email":
                    code = ErrorCode.EMAIL.getCode();
                    description = ErrorCode.EMAIL.getDescription();
                    break;
            }
        }

        return new ErrorResponse(code, description, errorMessage);
    }
}

 

  • Controller에서 @Valid 어노테이션이 붙은 값이 넘어왔을때 DTO에서 지정해준 조건에 맞지 않는다면 ExceptionController의 makeErrorResponse 함수가 작동하게 되고, 프론트엔드로 ErrorCode에서 지정해준 값과 DTO에 지정해준 Message가 Response Body에 담겨 프론트엔드로 응답하게 됩니다.

반응형

'Study > Spring' 카테고리의 다른 글

[Spring] QueryDSL-JPA  (0) 2024.12.30
[Spring] @InitBinder  (0) 2021.08.08
[Spring] @Data 어노테이션  (0) 2021.06.30
[Spring] 영속성 컨텍스트 (Persistence Context)  (0) 2021.06.30
[Spring Boot] Spring 프레임워크  (2) 2021.06.26

+ Recent posts