slack.token = {slack에서 제공하는 app 토큰} (ex, xoxp- ....)
slack.channel = {공지를 보낼 채널} (ex, #notice)
@Service
public class SlackService {
@Value(value = "${slack.token}")
String token;
@Value(value = "${slack.channel}")
String channel;
public void postSlackMessage(String message) {
try {
MethodsClient methods = Slack.getInstance().methods(token);
ChatPostMessageRequest request = ChatPostMessageRequest.builder()
.channel(channel)
.text(message)
.build();
methods.chatPostMessage(request);
} catch (SlackApiException | IOException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage());
}
}
}
@RestController
public class QnAController {
@PostMapping("/QnAs/QnA")
public QnADto createQnA(@RequestHeader("Authorization") String userToken, @RequestBody QnADto qnADto) {
slackService.postSlackMessage("새로운 QnA가 생성되었습니다.");
return qnAService.createQnA(userToken, qnADto);
}
...
}
😎 어노테이션에 대한 자세한 설명은 [CowAPI] User 코드 리뷰 에서 확인할 수 있습니다.
@Api(tags = {"QnA 게시판"})
@RequestMapping(value = "/api/v1")
@RestController
@RequiredArgsConstructor
public class QnAController {
private final QnAService qnAService;
@PostMapping("/QnAs/QnA")
public QnADto createQnA(@RequestHeader("Authorization") String userToken, @RequestBody QnADto qnADto) {
return qnAService.createQnA(userToken, qnADto);
}
@GetMapping("/QnAs/{QnAId}")
public QnADto readQnA(@RequestHeader("Authorization") String userToken, @PathVariable(value = "QnAId") Long qnAId) {
return qnAService.readQnA(userToken, qnAId);
}
@PutMapping("/QnAs/QnA")
public QnADto readQnA(@RequestHeader("Authorization") String userToken, @RequestBody QnADto qnADto) {
return qnAService.updateQnA(userToken, qnADto);
}
@DeleteMapping("/QnAs/QnA")
public QnADto deleteQnA(@RequestHeader("Authorization") String userToken, @RequestBody QnADto qnADto) {
return qnAService.deleteQnA(userToken, qnADto);
}
@GetMapping("/QnAs/QnA/search")
public QnAListDto searchQnA(@RequestParam(value = "query") String query) {
return qnAService.searchQnA(query);
}
@GetMapping("/QnAs/QnA/page")
public QnAListDto QnAPage(@RequestParam(value = "page") Long page) {
return qnAService.pageQnA(page);
}
}
😎 API 문서 : Swagger
😎 QnA 생성, 조회, 수정, 삭제는 Jwt 토큰을 받습니다.
@Service
@RequiredArgsConstructor
@Transactional
public class QnAService {
private final QnARepository qnARepository;
private final UserRepository userRepository;
public QnADto createQnA(String userToken, QnADto qnADto) { ... }
public QnADto readQnA(String userToken, Long qnAId) { ... }
public QnADto updateQnA(String userToken, QnADto qnADto) { ... }
public QnADto deleteQnA(String userToken, QnADto qnADto) { ... }
public QnAListDto searchQnA(String query) { ... }
public QnAListDto pageQnA(Long pageId) { ... }
😎 JWT 토큰 인증 및 인가 코드 수정하고 있습니다.
😎 위의 함수들은 각각의 기능에 따른 필요한 로직을 수행합니다.
😎 실제 DB의 User Table에는 QnAId가 존재하지 않고 Join을 통해서 가져옵니다.
😎 따라서 DB에서 가져올 때 역직렬화를 무시하고 클라이언트로 전송할때 직렬화를 무시하기 위해 사용합니다.
@Entity
@DynamicUpdate
public class QnA {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
@ManyToOne
@JoinColumn(name = "User_email")
private User user;
}
@Entity
@DynamicUpdate
public class User {
...
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
@JsonIgnore
List<QnA> QnAs = new ArrayList<>();
}
😎 Id를 Auto Increment로 지정합니다.
😎 User와 매핑합니다.
@Repository
public interface QnARepository extends JpaRepository<QnA, Long> {
@Query(value =
"Select * From QnA q Where (q.title Like %:query% Or q.content Like %:query%) " +
"And q.isDeleted != TRUE " +
"Limit :searchCnt", nativeQuery = true)
List<QnA> searchByQuery(@Param("query") String query, @Param("searchCnt") Long searchCnt);
@Query(value =
"Select * From QnA q Where q.isDeleted != TRUE " +
"Order by q.updatedDate DESC, q.id ASC " +
"Limit :pageId, :pageCnt", nativeQuery = true)
List<QnA> findByPage(@Param("pageId") Long pageId, @Param("pageCnt") Long pageCnt);
...
}
😎 @Query를 통해 직접 SQL을 작성하여 검색과 페이지네이션 했습니다.
😎 JpaRepository에 PagingAndSortingRepository가 있음을 확인했습니다.
😎 리펙토링시 수정할 예정입니다.
😎 User Test Code 와 동일한 구조로 리펙토링 예정입니다.