아직 완성하진 못했지만 내 코드에 정말 정말 많은 일이 있었고.. 많은 것을 배웠다. 과정을 하나 하나 기록하지 못한 것이 아쉽지만 기억을 짜내서 최대한 제작 과정을 작성해보려한다.
다른 사람의 코드를 보고 늘 새로운 기술이 보이면 일단 도입해보려 시도중이다. 결과적으로 매일 하루에 한개 이상의 낯선 기술을 내 코드에 집어넣고있다. 시야가 넓어지니 프로그램의 흐름도 훨씬 잘 이해할 수 있다.
1. 무작정 db 생성
일단 '어떤 어노테이션이 있는가' 를 중점적으로 학습했다. 어노테이션은 선대 프로그래머들이 spring을 사용하며 불편했던 기능들을 한 단어로 압축시켜 놓은거라 많이 아는게 힘이라고 생각한다.
@Entity //entity임을 spring container에 알림
@AllArgsConstructor //필드의 모든 생성자 자동 생성
@NoArgsConstructor //빈 기본 생성자 생성(Reflection을 사용하는 JPA 때문에)
@Getter //모든 객체에 getter 생성
@Setter //모든 객체에 setter 생성
@Builder //builder 패턴 내장되어있음
@EntityListeners(AuditingEntityListener.class) //자동으로 값 추가
@Table(name = "board_db") //table name 명명
....
@Builder.Default //build 할 때 default 값을 0으로 정함
@Column(nullable = false) //null이 될 수 없음
private int count = 0; //null이 될 수 없으므로 원시값인 int
@OneToMany(mappedBy = "board", cascade = CascadeType.ALL, orphanRemoval = true)
//하나의 부모 board에 여러 자식 comment가 매핑 되는 관계
//부모 엔티티의 변경이 자식에게도 반영
//부모가 삭제되면 자식도 삭제
@JsonManagedReference
//json값을 주고받을 때의 순환 참조(스택 오버플로우) 막음
private List<CommentDb> comments = new ArrayList<>();
@CreatedDate //날짜 자동생성
private LocalDateTime createdDate;
이처럼 어노테이션의 종류를 공부하다 보면 프로그램의 실행 흐름에 관해서도 자연스레 공부하게 될 수밖에 없었다.
entity로 이 클래스의 역할을 알리는구나 -> 다른 주요 클래스들도 이같은 어노테이션이 존재하겠구나
getter, setter, builder 를 사용하는구나 -> 이곳의 정보들로 클라이언트와 상호작용을 하는구나
jsonmanagedreference -> json으로 정보교환을 하며 메모리를 사용하는구나
noargsconstructor -> 왜 비어있는 기본 생성자를 사용하는거지? -> JPA는 리플렉션을 통해 ORM을 수행하는구나 -> 리플렉션과 ORM이 뭐지?
2. 파일 만들기
github에서 다른 백엔드 spring 프로젝트를 참고해 패키지 분리부터 했다. 이 과정에서 프로그램이 어떤 흐름으로 실행되는지, 어디에서 인터페이스를 적용하면 좋은지 등을 알 수 있다.
인터페이스를 사용하고 있다 -> 하나 이상의 같은 동작을 수행하는 메서드들이 있구나
dto 발견 -> dto가 뭐지? -> entity값을 안전하게 지켜주는구나 -> 클라이언트와 직접적인 상호작용에서는 dto를 사용하는구나
사용자 설정 예외처리 패키지파일 발견 -> 아 이걸로 404, 403등 에러코드를 반환하는구나 -> 일단 저장
swagger 발견 -> api를 쉽게 작성, 테스트하게 해준다고? -> 일단 적용
redis 발견 -> 캐싱을 사용할 수 있다고? -> 일단 적용
ide에서 docker로 실행하는 옵션 발견 -> 그 고래 어플을 내가 쓸 수 있다고? -> 일단 써봄
쓰고보니 되게 무지성 박치기공룡같은데 하핳 나에겐 이 공부법이 맞는거같다
3. 서비스 계층 빌드(DTO 도입)
서비스계층이 총 3개로 나뉘어져 있던 걸 기능별로 쪼개봤다. 비밀번호 검증 로직이 필요한 클래스들은 따로 추상 클래스로 분류하고, 페이징이 필요한 클래스들도 인터페이스로 나눴다.
그런데 이렇게 하니 문제가 생겼다. DTO를 도입하는 과정에서 코드가 어지러워졌고.. 추상클래스는 따로놀고 파라미터도 불분명하고.. 해결책이 필요했다.
그 과정에서 dto와 entity 변환 과정을 쉽게 다룰 수 있는 builder + mapper 클래스 활용법을 알게됐다.
public class BoardMapper {
//엔티티 -> DTO
public static BoardResponseDto toResponseDto(BoardDb boardDb){
//좋아요 10 이상이면 앞에 불 이모지
String hotTitle = boardDb.getLiked() > 10 ? "\uD83D\uDD25 " + boardDb.getTitle() : boardDb.getTitle();
return BoardResponseDto.builder()
.id(boardDb.getId())
.title(hotTitle)
.content(boardDb.getContent())
.nickname(boardDb.getNickname())
.category(boardDb.getCategory())
.password(boardDb.getPassword())
.liked(boardDb.getLiked())
.count(boardDb.getCount())
.createDate(boardDb.getCreatedDate())
.build();
}
//DTO -> 엔티티
public static BoardDb fromRequestDto(BoardRequestDto boardRequestDto){
return BoardDb.builder()
.title(boardRequestDto.getTitle())
.content(boardRequestDto.getContent())
.nickname(boardRequestDto.getNickname())
.password(boardRequestDto.getPassword())
.category(Category.valueOf(boardRequestDto.getCategory().toUpperCase()))
.build();
}
}
빌더를 활용해서 dto와 entity의 변환 과정을 쉽게 캡슐화 할 수 있었다. 변환과정을 캡슐화 하니 service, controller 코드 길이도 자연스레 줄었고, 흩어져있던 service class들을 하나의 class로 합쳤다
mapper클래스를 자동으로 생성해주는, mapstruct라는 라이브러리도 있었지만 mapper에 대한 개념도 불확실해서 일단 나중으로 미뤄뒀다.
이렇게 지금까지 구현된 걸 도식화하면
이런 느낌
짠! swagger로 테스트를 해봤는데 아주 잘 된다.
DB에도 정상적으로 들어왔다. 뿌듯뿌듯
이제 comment api도 만들고, redis cache 적용하고, 비밀번호 암호화까지 한번 달려보자!
'개인 프로젝트 > crud-api-project' 카테고리의 다른 글
리눅스로 개발환경 옮기기(Docker, Postgresql, Redis) -2 (0) | 2024.11.29 |
---|---|
리눅스로 개발환경 옮기기(Docker, Postgresql, Redis) -1 (0) | 2024.11.28 |
DB 구조 설계하기 (0) | 2024.11.28 |