본문 바로가기

Backend

[SpringBoot] 게시판 만들기 (advanced type)

반응형

구상중인 게시판 기능

  • 회원가입 ( 중복되는 닉네임 불가)
  • 로그인 & 로그아웃
  • 게시판 종류를 3-4가지로 구분 ( 회원 소개 글, 자유게시판, 질문 게시판, 관리 게시판)
  • 오른쪽 하단 메뉴 중에 현재 접속 중인 회원 목록 표시 (웹 소켓 활용 예정)
  • 게시글들에는 시간순, 댓글 갯수 순, 게시글 추천 수로 정렬
  • 구글로 로그인하면 gpt 대화창 하나 표시 (가능할지는 의문)
  • 글 쓰기 (제목, 내용, 이미지 업로드) (로그인한 유저만 가능)

 


  • 활용 Annotation
    • @Entity : JPA에서 엔티티 클래스를 나타내는 어노테이션이다. 즉 해당 어노테이션이 붙은 클래스는 데이터베이스의 데이블과 매핑되는 클래스로, JPA가 본 클래스를 영속성 컨텍스트에서 관리할 수 있게 한다.
    • @AllArgsConstructor : Lombok 라이브러리의 어노테이션으로, 모든 필드의 매개변수로 받는 생성자를 자동으로 생성한다. 즉, 모든 필드 값에 대한 생성자가 자동으로 생성된다. 
    • @NoArgsConstructor : Lombok 라이브러리의 어노테이션으로,  매개변수가 없는 기본 생성자를 자동으로 생성한다. 
    • @Builder : Lombok 라이브러리의 기능으로, 빌더패턴을 생성하는데 사용된다. 여러개의 필드값을 업데이트한 객체를 생성할 때 활용한다.
    • @Getter : Lombok의 어노테이션으로, 모든 필드에 대한 Getter 메소드를 자동으로 생성한다.
    • @GeneratedValue(strategy = GenerationType.IDENTITY) : JPA에서 엔티티의 기본키 값을 자동으로 생성하기 위해 사용되는 엔티티이다. 일반적으로 IDENTITY 전략을 활용하고, 이는 숫자로 된 기본키에 사용되고 자동증가 방식을 사용한다.
    • @Enumerated(EnumType.STRING) : JPA에서 Enum 타입을 매핑할 때 사용되는 어노테이션이다. enum 필드를 테이블의 열로 매핑할 때, 해당 enum 값이 string 형태로 저장되도록 지정한다.
    • @RequestParam("") : http요청의 파라미터 값을 매개변수로 바인팅하기 위해 사용한다. 괄호 안에는 바인딩하고자 하는 key값을 입력한다. 
    • @RequestBody : http요청 메시지의 body에 담긴 json 데이터를 객체로 변환하기 위해 보통 활용한다. 
    •  

 

step1과 다르게 UI를 제작하는데 시간이 많이 소요되어서 POSTMAN을 활용한 API테스트 혹은 테스트코드를 작성하여 동작들을 검증하고자 한다.


  • API를 통한 json 데이터를 주고받기도 결정했기 때문에 오고가는 데이터 형식을 통일시키고자 Response class를 하나 만들었다. 서버에서 클라이언트로 응답하는 형태를 통일시켰다. 
public enum ApiStatus {
    SUCCESS, FAIL, ERROR
}
public record ApiResponse(
        ApiStatus status,
        String message,
        Object data
) {

    public static ApiResponse success(String message, Object data){
        return new ApiResponse(ApiStatus.SUCCESS, message, data);
    }
    public static ApiResponse success(String message){
        return new ApiResponse(ApiStatus.SUCCESS, message, null);
    }
    public static ApiResponse fail(String message){
        return new ApiResponse(ApiStatus.FAIL, message, null);
    }
    public static ApiResponse error(String message){
        return new ApiResponse(ApiStatus.ERROR, message, null);
    }
}

요청했던 동작이 성공했는지 혹은 실패했는지 혹은 추후에 토큰을 다루는 작업을 할 때 토큰이 만료된다면 error까지 추가하여 구현하고자 위와 같이 작성했다. 


  • 회원가입 과정 구현

회원가입을 진행할 때는 아이디, 비밀번호, 닉네임을 기입하게끔 구상했다. 중간에 아이디와 닉네임은 중복이 되면 안되기 때문에 중복검사하는 api도 하나씩 구현해두었다.

@Data
public class UserSignupRequest {

    private String loginId;
    private String password;
    private String nickname;

    public User ToEntity(){
        return User
                .builder()
                .loginId(loginId)
                .password(password)
                .nickname(nickname)
                .userRole(UserRole.ASSOCIATE)
                .signUpAt(LocalDateTime.now())
                .recommendCnt(0)
                .build();
    }
}

body에 json 포맷으로 아이디,비밀번호,닉네임이 담긴 채 request되면 다음 url을 바탕으로 라우팅이 되어 각각의 로직이 실행된다.

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

    private final UserService userService;

    /** 회원가입 절차*/
    @GetMapping("/checkNickname") //닉네임 중복검사
    public ApiResponse checkNickname(@RequestParam("nickname") String nickname){
        return userService.checkSignUpNickname(nickname);
    }

    @GetMapping("/checkId") // 아이디 중복검사
    public ApiResponse checkSignupId(@RequestParam("signupId") String signUpId){
        return userService.checkSignUpId(signUpId);
    }

    @PostMapping("/signup") //회원가입 버튼 누를 때
    public ApiResponse register(@RequestBody UserSignupRequest userSignupRequest){
        User user = userSignupRequest.ToEntity();
        return userService.register(user);
    }
}
@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;

    public ApiResponse checkSignUpNickname(String singUpNickname){
        boolean res = !userRepository.existsByNickname(singUpNickname);
        // 존재하면 0, 가능하면 1리턴
        if(res){
            return ApiResponse.success("OK");
        }
        else {
            return ApiResponse.fail("NotOK");
        }
    }

    public ApiResponse checkSignUpId(String signUpId){
        boolean res = !userRepository.existsByLoginId(signUpId);
        // 존재하면 0, 가능하면 1리턴
        if(res){
            return ApiResponse.success("OK");
        }
        else {
            return ApiResponse.fail("NotOK");
        }
    }

    public ApiResponse signup(User user){ //회원가입
        User save = userRepository.save(user);
        return ApiResponse.success("signupSuccess");
    }
 }

 

다음은 회원가입 과정 구현 테스트이다. 비어있는 db에서 시작한다.

get 방식으로 닉네임 중복검사 url에 파라미터를 추가하여 날렸을 때,

데이터베이스가 비어있기 때문에 닉네임 사용이 가능하다는 의미에서 ok를 응답하게 구현했다.

다음은 회원가입 data request 검증이다. 

body의 데이터와 준회원을 의미하는 회원등급, 회원가입 시간을 추가로 넣어서 레코드를 구성시켰다. 

이후 다시 닉네임 중복 과정을 진행해보면, '규규'가 이미 존재하기 때문에 FAIL메시지를 받게 된다.


  • 로그인 과정 구현 

회원가입 과정과 유사하게 DTO Class를 하나 만들고 로그인 정보를 가진 객체를 만들어 db에서 아이디가 존재하는지 먼저 확인한 이후, 비밀번호와 일치하는지 검증을 마친 후 Response msg로 응답한다. 


이후부터는 Security를 공부한 이후에 토큰 기반 인증과 함께 진행되어야 하는 부분이다. 로그인에 성공했기 때문에 사용자 정보를 서버가 알아야한다. 세션 기반으로 구상하려고 했지만, 이왕 Springboot를 깊게 공부해보는 기회삼아 토큰 기반으로 공부를 하고 구현해보고자 한다.

 

계속해서 추가해나아갈 깃허브 주소는 다음과 같다. branch는 advanced로 설정해야한다!!

https://github.com/hyeon-gyu/NoticeBoard.git 

 

GitHub - hyeon-gyu/NoticeBoard: 개인프로젝트 (게시판 구현)

개인프로젝트 (게시판 구현). Contribute to hyeon-gyu/NoticeBoard development by creating an account on GitHub.

github.com


ApiResponse class를 지정하여 나만의 리턴타입을 만들어서 활용하려고 계획을 했었다. 하지만 웹서핑 도중 ResponseEntity 즉 HttpEntity의 존재를 알게 되었다. 상태코드도 직접 개입할 수 있고, 결과 데이터도 잘 wrapping할 수 있는 활용도가 높은 방법를 활용해보기로 했다.

예를 들면 기존의 responseDto를 수정하여, controller단에서 닉네임 중복검사 함수를 다음과 같은 방향으로 전면 수정하였고 이렇게 적용할 계획이다.

    @GetMapping("/checkNickname") //닉네임 중복검사
    public ResponseEntity<?> checkDupNickname(@RequestParam("nickname") String nickname){
        userService.checkSignUpNickname(nickname);
        return ResponseEntity.status(HttpStatus.OK).build();
    }
반응형