본문 바로가기

[스프링부트/업무일지 만들기] 업무일지 API 만들기

ironwhale 2024. 6. 3.

제가 주 교재로 사용한 스프링 부트 3 백엔드 개발자 되기: 자바 편(저자: 신선영)에는 API 컨트롤러를 만들고 브라우저에 보여주는 컨트롤러도 만들어 각각 만들어서 프로젝트를 진행했습니다. 이 방식이 저에게 더 와 닿았던 이유는 웹 뿐만 아니라 앱에서도 해당 서버를 사용할 수 있을 거라 생각했기 때문입니다. 

 

아래 코드를 보시면 복잡한 코드는 사실 별로 없습니다. 그래서 전체 코드를 하나하나 설명하기 보다는 제가 이번 프로젝트를 통해 새로 배우거나 중요하다고 생각 한 부분을 설명드리겠습니다. 


@RequestMapping("/api")

이 애너테이션을 사용하면 중복을 줄일 수 있습니다. 가령 api/logs, api/logs/{id}와 같이 api가 계속 중복되는데 이것을 줄여주는 애너테이션 입니다. 

@PreAuthorize("isAuthenticated()")

사실 이 업무일지 프로젝트에서는 필요 없는 애너테이션일 수 있습니다. 이 업무일지는 로그인을 하지 않으면 사용할 수 없기 때문이죠 하지만 나중에 다른 프로젝트할 때를 위해 적어두었습니다. 이 애너테이션은 Principal 객체를 사용하기 위해서 사용한다고 보시면 됩니다.  Principal 객체는 사용자의 정보를 담고 있는 객체로 로그인을 해야지만 생성되는 객체입니다. 따라서 로그인 없이 해당 메소드를 사용하게 되면 에러가 나기 때문에 로그인 없이 사용하지 못하도록 방지하는 기능이 있는 애너테이션입니다. 

@PreAuthorize("isAuthenticated()")

사실 이 업무일지 프로젝트에서는 필요 없는 애너테이션일 수 있습니다. 이 업무일지는 로그인을 하지 않으면 사용할 수 없기 때문이죠 하지만 나중에 다른 프로젝트할 때를 위해 적어두었습니다. 이 애너테이션은 Principal 객체를 사용하기 위해서 사용한다고 보시면 됩니다.  Principal 객체는 사용자의 정보를 담고 있는 객체로 로그인을 해야지만 생성되는 객체입니다. 따라서 로그인 없이 해당 메소드를 사용하게 되면 에러가 나기 때문에 로그인 없이 사용하지 못하도록 방지하는 기능이 있는 애너테이션입니다. 

@PathVariable

아마 자주 만나게 될 애너테이션인데 "/logs/{id}" 이렇게 id 값을 가지고 오기 위해 사용합니다. 

@RequestBody

post 요청으로 오는 데이터를 가지고 올때 사용하는 애너테이션 입니다. 제가 주교재로 사용한 책과 같이 필요한 데이터만 입력받도록 DTO를 만들어서 사용했는데 여기 보시면 작성일시와 id와 같이 자동으로 데이터베이스에 저장되는 값들은 없는 것을 보실 수 있습니다. 맨 아래 업무일지의 모든 코드가 있는 깃허브 주소를 남겨두었으니 자세한 코드를 보시고 싶으신 분들으 참조하시기 바랍니다. 

@Value
public class SaveWorkLogRequest implements Serializable {
    @NotEmpty(message = "필수 입력입니다.")
    String log;
    String userName;
    List<String> tags;
}

 

여기까지 API 서버를 만들는 부분입니다. 다음은 뷰컨트롤러를 만들어 실제 웹에 데이터를 뿌려주는 방법에 대해 알아보겠습니다. 


전체코드

@RequestMapping("/api")
@RequiredArgsConstructor
@RestController
public class WorkLogApiController {

    private final WorkLogService workLogService;

    @PreAuthorize("isAuthenticated()")
    @GetMapping("/logs")
    public ResponseEntity<List<WorkLogResponse>> findAllLogs(@RequestParam(required = false) String keyword, Principal principal) {

        System.out.println("principal.getName() = " + principal.getName());
        if (keyword != null) {
            // 대소문자 구분 없이 검색 결과를 합치기 위해 Set 사용
            Set<WorkLogResponse> workLogs = new HashSet<>();

            workLogs.addAll(
                    workLogService.findByKeyword(keyword, principal.getName()).stream()
                            .map(WorkLogResponse::new)
                            .toList()
            );

            workLogs.addAll(
                    workLogService.findByKeyword(keyword.toUpperCase(), principal.getName()).stream()
                            .map(WorkLogResponse::new)
                            .toList()
            );
            return ResponseEntity.ok().body(new ArrayList<>(workLogs));
        } else {
            List<WorkLogResponse> workLogs = workLogService.findAll(principal.getName()).stream()
                    .map(WorkLogResponse::new)
                    .collect(Collectors.toList());
            return ResponseEntity.ok().body(workLogs);
        }
    }

    @PreAuthorize("isAuthenticated()")
    @PostMapping("/logs")
    public ResponseEntity<WorkLog> saveWorkLog(@Validated @RequestBody SaveWorkLogRequest request, Principal principal) {

        WorkLog workLog = WorkLog.builder().tags(request.getTags())
                .log(request.getLog())
                .userName(principal.getName()).build();
        workLogService.saveLog(workLog);
        return ResponseEntity.status(HttpStatus.CREATED).body(workLog);
    }

    @PreAuthorize("isAuthenticated()")
    @PutMapping("/logs/{id}")
    public ResponseEntity<WorkLog> updateWorkLog(@PathVariable Long id, @RequestBody UpdateWorkLogRequest request, Principal principal) {
        System.out.println("update principal = " + principal.getName());

        if (principal.getName() == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

        try {
            WorkLog workLog = workLogService.updateWorkLog(id, request, principal.getName());
            return ResponseEntity.ok().body(workLog);
        } catch (SecurityException e) {
            System.out.println("e = " + e.getMessage());
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(null);
        } catch (IllegalArgumentException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
        }

    }

    @DeleteMapping("/logs/{id}")
    public ResponseEntity<Void> deleteWorkLog(@PathVariable Long id) {
        workLogService.deleteWorkLog(id);
        return ResponseEntity.ok().build();
    }


}

깃허브 주소

https://github.com/ironwhale1014/work-log

댓글