4 분 소요



현재 글은 User 서비스 글의 뒷 내용을 이어갑니다.


👨‍💻 User CRUD 개발과정 순서

  1. Repository :

    데이터베이스에 접근하여 데이터를 다루는 기능을 담당하는 Repository를 먼저 구현.

  • JpaRepository를 추가함으로써 USER CRUD 기능을 쉽게 사용하게 함.
  • email로 user찾기 / username으로 user찾기 / id값에 해당하는 user찾기 메서드 추가.
  1. Entity :

데이터베이스의 테이블과 매핑되는 객체로, 테이블의 컬럼들과 매핑되는 필드를 가지고있음.

  • User
  • Reputation
  1. DTO :

데이터를 전송하기 위한 객체로, Entity와 비슷하지만 HTTP 요청과 응답에서 사용하는 데이터를 가지고 있음.

  1. Service :

비즈니스 로직을 처리하는 컴포넌트로, Repository를 사용하여 데이터를 조작하고, 비즈니스 로직에 맞게 데이터를 가공한다.

  1. Controller :

HTTP 요청을 처리하는 컴포넌트로, 클라이언트로부터 요청을 받아 Servcie에게 전달하고, Service가 처리한 결과를 다시 클라이언트에게 반환한다.

  1. Mapper :

Entity와 DTO 간의 변환을 담당하는 컴포넌트로, Entity를 DTO로 변환하거나, DTO를 Entity로 변환하는 등의 작업을 함




📃 User Controller의 작성방법

**Spring Framwork를 사용해서 RESTful API 서비스에서 **

User와 관련된 요청을 처리하는 controller 클래스를 작성하면된다.


컨트롤러 코드를 작성할 때는 먼저 해당 컨트롤러에서 제공해야 할 API의 요구사항과 목적을 정확히 파악해야 합니다. 그리고 요구사항과 목적에 맞게 URL과 HTTP Method를 결정하고, API 요청/응답에 필요한 데이터 형식과 필드를 정의합니다.

일반적으로 RESTful API에서는 URL의 경로 부분으로 자원(resource)을 표현하고, HTTP Method로 해당 자원에 대한 동작을 나타냅니다. 예를 들어, /users 경로에 GET 요청을 보내면 모든 사용자 정보를 가져오는 API를 호출하는 것입니다. 이와 같이 자원과 동작을 결정한 후, 해당 API에서 필요한 요청 데이터와 응답 데이터를 정의합니다.

따라서 컨트롤러 코드를 작성할 때는 먼저 요구사항과 목적을 파악하고, API 요청/응답에 필요한 데이터 형식과 필드를 정의하는 것이 중요합니다. 이를 기반으로 URL과 HTTP Method를 결정하고, 해당 동작에 필요한 비즈니스 로직을 작성합니다.


Controller 작성을 위한 요구사항 정의서



❓ User Controller에 작성되어야 할 내용?

User Controller에서는 사용자 관련 API를 구현합니다. 사용자의 생성, 수정, 조회 등을 담당합니다.

현재 진행중인 스택오버플로우 클론코딩에서는 다음과 같은 API가 구현되어야 한다.

  1. 사용자 생성 API : POST /users
  2. 사용자 정보 수정 API : PATCH /users
  3. 사용자 목록 조회 API : GET /users
  4. 타 사용자 정보 조회 API : GET /users/{user-id}
  5. 계정 소유자 정보 조회 API : GET /users/principal

위 API를 구현하기 위해서는 각각의 API마다 요청 파라미터와 반환값이 정해져 있습니다. 또한 각 API에서 처리되는 로직에 따라 필요한 서비스 클래스와 매퍼 클래스 등을 호출하고 반환값을 생성해야 합니다.

스택오버플로우 클론코딩에서는 Spring Boot를 사용하기 때문에, User Controller 클래스에는 @RestController 어노테이션을 선언하여 RESTful API를 구현합니다. 각 API마다 HTTP 메소드와 URL 매핑을 위해 @PostMapping, @PatchMapping, @GetMapping, @PathVariable, @RequestParam 등의 어노테이션을 사용합니다. 또한 요청 파라미터를 검증하기 위해 @Validated와 @Valid 어노테이션을 사용합니다.

이러한 방식으로 API를 구현하면, 클라이언트에서 해당 API를 호출하면 서버에서 해당 요청을 처리하고 JSON 형태의 응답을 반환합니다. 클라이언트는 이 응답을 받아서 처리합니다. 이렇게 API를 구현하면, 클라이언트와 서버 간의 통신이 효율적이고 빠릅니다.



RESTful API?

관련 링크↑


작성하면서 check할 point들

  • @Controller와 달리 @RestController 어노테이션을 사용하여 해당 클래스가 RESTful 웹서비스의 Controller 클래스임을 선언합니다.

    @RequestMapping 어노테이션을 이용하여 “/users”라는 경로로 들어오는 요청에 대해 매핑합니다.

    클래스 내부에서는 다양한 HTTP Method에 대한 요청을 처리하는 메서드들이 정의되어 있습니다.

    • @PostMapping 어노테이션을 이용한 회원 가입을 처리하는 메서드
    • @GetMapping 어노테이션을 이용한 이메일 인증 처리 메서드
    • @PatchMapping 어노테이션을 이용한 사용자 정보 수정 처리 메서드
    • 두 개의 @GetMapping 어노테이션을 이용한 사용자 목록 조회 및 특정 사용자 조회 처리 메서드
    • @GetMapping 어노테이션을 이용한 계정 소유자 정보 조회 처리 메서드

    각 메서드에서는 클라이언트가 요청한 데이터를 파라미터로 받아와서 해당 요청을 처리하고, 결과를 ResponseEntity 객체로 반환합니다. 이때 ApiResponse 객체를 이용하여 반환할 데이터를 담습니다.



❓ Controller와 RestController의 차이점

@Controller는 View를 반환하기 위한 컨트롤러 어노테이션입니다. 주로 서버에서 클라이언트에게 HTML, JSP 등의 View를 반환하기 위해 사용됩니다.

반면 @RestController는 RESTful API를 위한 컨트롤러 어노테이션입니다. 즉, 주로 JSON 형태의 데이터를 반환하기 위해 사용됩니다.

@Controller와 달리 @RestController는 메서드마다 ResponseBody 어노테이션을 붙일 필요가 없으며, 메서드가 반환하는 객체는 자동으로 JSON 형태로 반환됩니다.

따라서, @RestController를 사용하면 컨트롤러에서 JSON 형태의 데이터를 더욱 쉽게 반환할 수 있고, 클라이언트에서는 JSON 형태의 데이터를 더욱 쉽게 처리할 수 있습니다.

  • 결과적으로 JSON형태로 데이터를 보낼거기에 @RestController을 사용할것임.




⭐ User Controller 코드

@Slf4j
@RestController
@Validated
@RequestMapping("/users")
public class UserController {

    private final UserMapper userMapper;
    private final UserService userService;

    public static final String DEFAULT_URI = "/users";


    public UserController(UserMapper userMapper, UserService userService) {
        this.userMapper = userMapper;
        this.userService = userService;
    }

    /*
    폼 로그인 회원등록
     */
    @PostMapping
    public ResponseEntity<?> postUser(@Valid @RequestBody UserDto.Post userPostDto) throws MessagingException {

        User user = userMapper.userPostToUser(userPostDto);
        user.setReputation(new Reputation());

        User createdUser = userService.createUser(user);

        URI uri = UriUtil.createUri(DEFAULT_URI, createdUser.getUserId());

        return ResponseEntity.created(uri).body(ApiResponse.created());
    }

    @GetMapping("/confirm-email")
    public String verifyAccount(@Valid @RequestParam String token) {
        userService.confirmEmail(token);

        return "이메일 인증이 완료되었습니다.";
    }

    /*
    PATCH - 사용자 정보 수정
     */
    @PatchMapping
    @PreAuthorize("isAuthenticated()")
    public ResponseEntity<?> patchUser(@Valid @RequestBody UserDto.Patch userPatchDto, @AuthenticationPrincipal PrincipalDetails userDetails) {
        User user = userMapper.userPatchToUser(userPatchDto);
        user.setUserId(userDetails.getUserId());

        User updatedUser = userService.updateUser(user);

        return ResponseEntity.ok().body(ApiResponse.ok("data", userMapper.userToUserResponse(updatedUser)));
    }

    /*
    GET - 사용자 목록 조회
     */
    @GetMapping
    public ResponseEntity<?> getUsers(@Positive @RequestParam int page) {
        Page<User> userList = userService.getUserList(page - 1);
        int totalPage = userList.getTotalPages();

        List<UserDto.Response> userResponseList = userMapper.userListToUserResponseList(userList);

        return ResponseEntity.ok().body(ApiResponse.ok("data", userResponseList, "totalPages", totalPage));
    }

    /*
    GET - 타 사용자 정보 조회
     */
    @GetMapping("/{user-id}")
    public ResponseEntity<?> getUser(@Positive @PathVariable("user-id") Long userId) {
        User user = userService.getUser(userId);

        return ResponseEntity.ok().body(ApiResponse.ok("data", userMapper.userToUserResponse(user)));
    }

    /*
    GET - 계정 소유자 정보 조회
     */
    @GetMapping("/principal")
    @PreAuthorize("isAuthenticated()")
    public ResponseEntity<?> getPrincipal(@AuthenticationPrincipal PrincipalDetails userDetails) {

        User user = userService.getUser(userDetails.getUserId());

        return ResponseEntity.ok().body(ApiResponse.ok("data", userMapper.userToUserResponse(user)));
    }

😉 어노테이션 쓰임새

  • @Slf4j( Simple Logging Facade for Java)는 자바 애플리케이션에서 사용하는 로깅 라이브러리 중 하나입니다. SLF4J는 로깅 프레임워크들의 추상화 계층을 제공하여, 다양한 로깅 시스템에 대한 대체 가능성을 제공합니다.

    즉, 로깅 라이브러리들을 표준화된 인터페이스를 통해 간편하게 사용할 수 있도록 해주는 라이브러리입니다. @Slf4j 어노테이션은 롬복(Lombok) 라이브러리에서 제공하는 어노테이션 중 하나로, SLF4J를 이용하여 로그를 출력할 수 있게 해줍니다. 해당 어노테이션을 사용하면 자동으로 로거(logger)를 생성해주므로 개발자가 따로 로거를 생성하지 않아도 됩니다.

❓ 왜 로그를 남기려 할까?

로그는 소프트웨어 시스템에서 발생하는 이벤트와 관련된 정보를 기록하는 기술입니다. 이벤트는 프로그램 실행 중 발생하는 오류, 경고, 디버깅 정보 등을 포함합니다.

로그를 출력하는 이유는 여러 가지가 있습니다. 예를 들어, 어플리케이션에서 발생하는 오류를 찾기 위해서는 로그를 살펴보는 것이 도움이 됩니다. 또한, 어플리케이션의 동작 상태를 파악하거나, 디버깅 정보를 확인하기 위해서도 로그를 출력합니다.

로그는 어플리케이션 개발 단계에서도 중요한 역할을 합니다. 개발자는 로그를 통해 어플리케이션 동작 상태를 확인하고, 디버깅 정보를 파악할 수 있습니다. 따라서, 로그는 어플리케이션의 안정성과 유지 보수를 위해 매우 중요한 역할을 합니다.

댓글남기기