앞서 백엔드 분야에서 원활한 협업을 위해서 몇 가지 규칙을 정하게 됬다.
- 커밋 컨벤션
- branch 규칙
- main : 운영계 브런치
- hotfix : 치명적인 버그는 운영계 브런치에서 분기
- dev : 개발 브런치
- feature/{이름약자}/{기능명} : 기능 개발용 브런치
- bugfix/{이름약자}/{버그} : 오류 수정용 브런치
- main : 운영계 브런치
- 간단한 코드 리뷰
- 개인 PR 후 간단 하게 코드 리뷰 진행하기로
- merge 규칙은 Squash Merge로 진행
- Squash Merge?
- 각 기능 별로 작업한 내용을 하나의 커밋으로 묶음
- Squash Merge?
간단한 규칙을 정한 후, 프로젝트 초기 설정을 디스 코드에서 모여서 같이 진행하고 폴더 구조는 도메인 기준으로 나눠서 진행하기로 했다.
[개발 순서]
- 데이터 모델링
- 개발 전 API 설계
- 실 개발
데이터 모델링은 사용자 플로우와 피그마를 확인해서 필요한 데이터를 뽑아냈는데 MBTI 로직 설계 단에서 생각해 둔 것 처럼 질문 ,답변 테이블을 별도로 구성했다.
MBTI 검사를 하면서 필요한 테이블은 아래와 같다.
- 테스트 유형을 저장할 수 있는 테이블
- 테스트를 위한 질문 테이블
- 테스트 질문 별 답변 테이블
- 테스트 결과 테이블
- 테스트 결과 기록 테이블
결과 데이터들을 화면에 하드 코딩하지 않고 서버에서 DB에 정보를 저장하기로 해 결과 페이지에 해당하는 데이터들은 모두 관리 되어야했는데 결과의 컨텐츠 수는 모두 달라 컨텐츠를 효과적으로 관리하는 방법을 생각해봤다.
- 컨텐츠를 관리하는 테이블을 만들어서 맵핑하는 형식?
- 테이블이 많아지면 조인이 너무 늘어진 않을까?
- 콤마(,) 를 사용해서 Split 하는 형식?
- Json 형식으로 서버에서 파싱하는 형태?
최종적으로 결과 데이터는 복잡하고 관리해야 할 데이터가 많아질 수 있어 추후에도 확장할 수 있도록 JSON 형식으로 관리하기로 결정했다.
대략적인 데이터 모델링을 끝내고 실제 개발을 들어가기 전 API 설계를 진행했다. 실제 개발에 들어가기 전에 노션에 정의를 하고 프론트앤드 분들에게 선 공유해 필요한 데이터가 없는지 확인하는 과정을 거쳤다.
결과 테이블 (mbti 유형 별 동물 특징)을 호출 시 응답 response는 아래와 같다.
{
"data": {
"name": "참매",
"endangeredGrand": 2,
"content": {
"contents": [
"차가워보이는 스타일이예요.",
"약속은 지키려고 하는거라고 생각해요.",
"간섭을 싫어해서 어쩌면 혼자 하는게 더 낫다고 생각할거예요.",
"현란한 공감은 어려워요.",
"남의 이야기에 관심이 별로 없어요.",
"표정관리가 쉽지 않아 감정이 잘 드러나는 편이예요.",
"갑작스러운 변화는 싫어요.",
"현실적인 것을 좋아해요."
],
"sub_contents": [
"멸종위기종 (취약)",
"산업화로 서식지 파괴",
"천연기념물 323-1호"
],
"compatibility": {
"best": {
"mbti_type": "ESTP",
"name": "검은머리물떼새",
"description": "이러쿵 저러쿵 해서 잘 맞음"
},
"worst": {
"mbti_type": "INFJ",
"name": "쇠검은머리쑥새",
"description": "이러쿵 저러쿵 해서 안 맞음"
}
}
}
},
"message": ""
}
content부분은 feature테이블에서 json 형태로 저장했기 때문에 Converter을 통해 json을 파싱해서 사용하는 형태로 구성을 했다.
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Features {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id", nullable = false)
private Category category;
private String name;
private Integer grade;
@Enumerated(EnumType.STRING)
@Column(columnDefinition = "enum")
private MbtiType type;
@Convert(converter = FeaturesContentConvert.class) // 컨버터
@Column(length = 2000)
private FeaturesAttribute Content;
}
@Converter
public class FeaturesContentConvert implements AttributeConverter<FeaturesAttribute, String> {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(FeaturesAttribute attribute) {
try {
return objectMapper.writeValueAsString(attribute);
} catch (JsonProcessingException e) {
throw new BusinessException(ErrorCode.NOT_EXISTS_INFO, e.getMessage());
}
}
@Override
public FeaturesAttribute convertToEntityAttribute(String dbData) {
try{
return objectMapper.readValue(dbData , FeaturesAttribute.class);
} catch (JsonProcessingException e) {
throw new BusinessException(ErrorCode.NOT_EXISTS_INFO, e.getMessage());
}
}
}
이 외, 필요한 데이터들은 조인해서 MBTI 로직 개발을 마무리했다.
추가적으로, 프론트분들과의 협업을 위해 Swagger을 설정하고, 전역으로 유효성을 처리했는데, 아쉬운 점이 있다면 시간이 부족해서 오류 코드를 정의를 못했고, 전역 처리 부분도 BusinessException 처리 밖에 하지 못했다. ㅠㅠ
package numble.mbti.global.exception;
import numble.api.dto.ApiErrorResponseDto;
import numble.api.dto.ApiResponseDto;
import numble.api.mbti.controller.response.MbtiFeaturesResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ApiResponseDto<MbtiFeaturesResponse>> handleBusinessException(BusinessException ex) {
var errorResponse = ApiResponseDto.<MbtiFeaturesResponse>create(null, ex.getErrorCode().getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiErrorResponseDto> handleException(Exception ex) {
var errorResponse = new ApiErrorResponseDto(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
}
}
[트로블 슈팅? 목록]
- Spring + Hibernate enum 타입 문제 해결
- ddl-auto 옵션 설정을 잘하자
'리뷰 > 강의 리뷰' 카테고리의 다른 글
넘블을 아시나요? 디프백 연계_ 내가 만드는 나만의 유형테스트 회고 (1탄) - 설계 (0) | 2023.07.05 |
---|---|
[인프런] 스프링 핵심 원리 - 기본편 후기 (0) | 2021.09.10 |
[프로그래머스] 단순 CRUD는 그만! 웹 백엔드 시스템 구현(Spring boot) 스터디 11기 리뷰 (0) | 2021.09.09 |