[감독버전] 개발 - 글쓰기 api 및 인가 적용

ohahsis·2024년 5월 13일
post-thumbnail

📍 개발 순서

  • 글쓰기 model과 dto 작성
  • 글쓰기 Repository 구현
  • 글쓰기 Service 구현
  • 글쓰기 Controller 구현

📍 개발

글쓰기 model과 dto 작성

Note domain 작성

src/main/java/ohahsis/dailydirector/note/domain/Note.java

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "note")
public class Note extends BaseEntity {

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

    private String title;

    private Boolean status; // 노트 완결 여부

    @Size(max = 4)
    private Set<String> contents = new HashSet<>();

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "id")
    private User user;

    @OneToMany(mappedBy = "note", cascade = CascadeType.ALL)
    private Set<NoteHashtag> noteHashtags = new HashSet<>();
}
  • NoteInnerNote(현 contents) 에 대한 고민
    • InnerNote 를 테이블을 분리할까 고민했는데, 내부 리스트 컬럼으로 둬도 될 것 같다.
    • 해시태그와 달리 외부에서 InnerNote 만 조회할 일은 없을 것 같고, 괜히 연산만 복잡할 것 같았다.

Hashtag domain 작성

src/main/java/ohahsis/dailydirector/note/domain/Hashtag.java

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "hashtag")
public class Hashtag {

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

    private String name;

    @OneToMany(mappedBy = "hashtag", cascade = CascadeType.ALL)
    private Set<NoteHashtag> noteHashtags = new HashSet<>();

}
  • hashtag 를 note 에 컬럼으로 추가할 수도 있었지만, 추후 커뮤니티버전에서 확장성을 위해(태그당 조회수 조회) 테이블로 분리했다.

NoteHashtag domain 작성

src/main/java/ohahsis/dailydirector/note/domain/NoteHashtag.java

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "note_hashtag")
public class NoteHashtag {

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

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "note_id")
    private Note note;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "hashtag_id")
    private Hashtag hashtag;

}

NoteSaveRequest dto 작성

src/main/java/ohahsis/dailydirector/note/dto/request/NoteSaveRequest.java

@Data
public class NoteSaveRequest {

//    @NotBlank(message = "내용을 입력해주세요.")
    private Set<String> contents;

    private Boolean status;

    private String title;

    private Set<String> hashtagNames;
}

NoteSaveResponse dto 작성

src/main/java/ohahsis/dailydirector/note/dto/response/NoteSaveResponse.java

@Data
@AllArgsConstructor
public class NoteSaveResponse {
    private Long note_id;
}

글쓰기 Repository 구현

NoteRepository 구현

src/main/java/ohahsis/dailydirector/note/infrastructure/NoteRepository.java

@Repository
@Transactional(readOnly = true)
public interface NoteRepository extends JpaRepository<Note, Long> {
}

HashtagRepository 구현

src/main/java/ohahsis/dailydirector/note/infrastructure/HashtagRepository.java

@Repository
@Transactional(readOnly = true)
public interface HashtagRepository extends JpaRepository<Hashtag, Long> {
    @Transactional
    Boolean existsByName(String name);
}

NoteHashtagRepository 구현

src/main/java/ohahsis/dailydirector/note/infrastructure/NoteHashtagRepository.java

@Repository
@Transactional(readOnly = true)
public interface NoteHashtagRepository extends JpaRepository<NoteHashtag, Long> {
}
  • NoteHashtag 또한 명확하게 분리된 하나의 기능이기 때문에 별도의 리포지토리를 생성한다.

글쓰기 Service 구현

노트 내용 최대 개수 작성

src/main/java/ohahsis/dailydirector/note/NoteConstants.java

public class NoteConstants {
    public static final int CONTENTS_MAX_SIZE = 4;
}

NoteService 구현

src/main/java/ohahsis/dailydirector/note/application/NoteService.java

@Slf4j
@Service
@RequiredArgsConstructor
public class NoteService {
    private final NoteRepository noteRepository;
    private final NoteHashtagRepository noteHashtagRepository;
    private final HashtagRepository hashtagRepository;

    @Transactional
    public NoteSaveResponse save(NoteSaveRequest request) {
        // 제목과 내용이 모두 없는 경우
        if (request.getContents().isEmpty() || request.getTitle().isBlank()) {
            throw new NoteSaveInvalidException(ErrorType.NOT_BLANK_ERROR);
        }

        // 기승전결 외 5개 이상의 문서 저장 요청이 온 경우
        if (request.getContents().size() > CONTENTS_MAX_SIZE) {
            throw new NoteSaveInvalidException(ErrorType.CONTENTS_MAX_SIZE_4);
        }


        /**
         * 노트 저장
         */
        var note = Note.builder()
                .contents(request.getContents())
                .status(request.getStatus())
                .title(request.getTitle())
                .build();

        var savedNote = noteRepository.save(note);
        
        
        /**
         * 해시태그 저장
         */
        for (String name : request.getHashtagNames()) {
            if(!hashtagRepository.existsByName(name)) {
                var hashtag = Hashtag.builder()
                        .name(name)
                        .build();
                hashtagRepository.save(hashtag);
            }
        }

        /**
         * 노트 해시태그 저장
         */
        for (String name : request.getHashtagNames()) {
            Hashtag hashtag = Hashtag.builder()
                    .name(name)
                    .build();
            var noteHashtag = NoteHashtag.builder()
                    .note(note)
                    .hashtag(hashtag)
                    .build();
            noteHashtagRepository.save(noteHashtag);
        }

        return new NoteSaveResponse(savedNote.getNote_id());
    }
}

추후 해시태그 서비스를 분리하는 것을 고려해보자.

글쓰기 Controller 구현

NoteController 구현 및 사용자 인가 적용

src/main/java/ohahsis/dailydirector/note/presentation/NoteController.java

@RestController
@RequestMapping(path = "/api/notes", produces = MediaType.APPLICATION_JSON_VALUE)
@RequiredArgsConstructor
public class NoteController {
    private final NoteService noteService;

    @PostMapping
    public ResponseEntity<?> createNote(AuthUser user, @RequestBody NoteSaveRequest noteRequest) {
        var response = noteService.save(noteRequest);
        return ResponseDto.created(response);
    }
}
  • AuthUser 를 파라미터에 포함시켜서 인가 resolver 가 작동하도록 한다.

📍 결과

POSTMAN 헤더 토큰 설정

Auth 설정 Header 확인
  • api 요청을 보내기 전, 토큰 상수의 값을 key 로, 로그인 했을 때 응답으로 받은 토큰 값을 value 로 하는 키-값을 헤더에 포함시킨다.
  • Header 를 보면 설정한 API Key 가 잘 적용된 것을 확인할 수 있다.

노트 정상 추가

노트 4개 초과 예외


참고 자료

profile
백엔드 개발자입니다.

0개의 댓글