Skip to content

Commit

Permalink
[FEAT] 유저 CRUD 구현 + 내 정보 API 구현 (#34)
Browse files Browse the repository at this point in the history
## 작업 내용
- 단순 User CRUD 구현 (`/api/users/`)
- 내 정보 API 반영 (`/api/users/me`)
- `MethodArgumentNotValidException` 에대한 Exception Handling 추가
- .env 에 대해 gitignore 추가

Closes #20
  • Loading branch information
JuneParkCode authored Aug 5, 2024
1 parent ded2ebb commit be296c7
Show file tree
Hide file tree
Showing 17 changed files with 816 additions and 126 deletions.
5 changes: 4 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
MYSQL_USERNAME=root
MYSQL_PASSWORD=1234
MYSQL_DATABASE=TALKKA_DB
MYSQL_URL=jdbc:mysql://localhost:3306/TALKKA_DB?createDatabaseIfNotExist=true
MYSQL_URL=jdbc:mysql://localhost:3306/TALKKA_DB?createDatabaseIfNotExist=true

NAVER_CLIENT_ID=CLIENT_ID
NAVER_CLINET_SECRET=CLIENT_SECRET
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ out/
### OS ###
.DS_Store

./data/mysql-data
./data/mysql-data

.env
.env.*
!.env.sample
2 changes: 0 additions & 2 deletions server/src/main/java/com/talkka/server/ServerApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class ServerApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
Expand All @@ -11,6 +12,7 @@
@ToString
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class ApiRespDto<T> {
private int statusCode;
private String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.talkka.server.common.exception.handler;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

Expand All @@ -10,6 +12,19 @@

@ControllerAdvice
public class RestControllerAdvice {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiRespDto<Void>> handleMethodArgumentNotValidException(
MethodArgumentNotValidException exception) {
ApiRespDto<Void> responseDto = ApiRespDto.<Void>builder()
.statusCode(400)
.message(exception.getBindingResult().getAllErrors().get(0).getDefaultMessage())
.build();

return new ResponseEntity<>(
responseDto,
HttpStatus.BAD_REQUEST
);
}

@ExceptionHandler(HttpBaseException.class)
public ResponseEntity<ApiRespDto<Void>> handleHttpException(HttpBaseException exception) {
Expand Down
9 changes: 9 additions & 0 deletions server/src/main/java/com/talkka/server/config/JpaConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.talkka.server.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class JpaConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.talkka.server.user.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.talkka.server.common.dto.ApiRespDto;
import com.talkka.server.common.enums.StatusCode;
import com.talkka.server.oauth.domain.OAuth2UserInfo;
import com.talkka.server.user.dto.UserCreateDto;
import com.talkka.server.user.dto.UserCreateReqDto;
import com.talkka.server.user.dto.UserDto;
import com.talkka.server.user.dto.UserRespDto;
import com.talkka.server.user.dto.UserUpdateReqDto;
import com.talkka.server.user.enums.Grade;
import com.talkka.server.user.service.UserService;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;

@GetMapping("/{user_id}")
public ResponseEntity<ApiRespDto<UserRespDto>> getUser(
@PathVariable("user_id") Long userId
) {
UserRespDto userRespDto = UserRespDto.of(userService.getUser(userId));
return ResponseEntity.ok(
ApiRespDto.<UserRespDto>builder()
.statusCode(StatusCode.OK.getCode())
.message(StatusCode.OK.getMessage())
.data(userRespDto)
.build()
);
}

// 현재는 AuthController 쪽에서 처리하고 있으나, 추후 FE 연결 이후 사용할 것임.
@PostMapping("")
public ResponseEntity<ApiRespDto<UserRespDto>> createUser(
@AuthenticationPrincipal OAuth2UserInfo userInfo,
@RequestBody @Valid UserCreateReqDto userCreateReqDto) {
UserCreateDto userCreateDto = new UserCreateDto(
userInfo.getName(),
userInfo.getEmail(),
userCreateReqDto.getNickname(),
userInfo.getProvider(),
userInfo.getAccessToken(),
Grade.USER
);
UserRespDto userRespDto = UserRespDto.of(userService.createUser(userCreateDto));
return ResponseEntity.ok(
ApiRespDto.<UserRespDto>builder()
.statusCode(StatusCode.OK.getCode())
.message(StatusCode.OK.getMessage())
.data(userRespDto)
.build()
);
}

@PutMapping("/{user_id}")
public ResponseEntity<ApiRespDto<UserRespDto>> updateUser(@PathVariable("user_id") Long userId,
@RequestBody @Valid UserUpdateReqDto userUpdateReqDto) {
UserRespDto userRespDto = UserRespDto.of(
userService.updateUser(userId, userUpdateReqDto));
return ResponseEntity.ok(
ApiRespDto.<UserRespDto>builder()
.statusCode(StatusCode.OK.getCode())
.message(StatusCode.OK.getMessage())
.data(userRespDto)
.build()
);
}

@DeleteMapping("/{user_id}")
public ResponseEntity<ApiRespDto<Long>> deleteUser(@PathVariable("user_id") Long userId) {
Long deletedUserId = userService.deleteUser(userId);
return ResponseEntity.ok(
ApiRespDto.<Long>builder()
.statusCode(StatusCode.OK.getCode())
.message(StatusCode.OK.getMessage())
.data(null)
.build()
);
}

@GetMapping("/me")
public ResponseEntity<ApiRespDto<UserRespDto>> getMe(@AuthenticationPrincipal OAuth2UserInfo userInfo) {
UserRespDto userRespDto = UserRespDto.of(userService.getUser(userInfo.getUserId()));
return ResponseEntity.ok(
ApiRespDto.<UserRespDto>builder()
.statusCode(StatusCode.OK.getCode())
.message(StatusCode.OK.getMessage())
.data(userRespDto)
.build()
);
}

@PutMapping("/me")
public ResponseEntity<ApiRespDto<UserRespDto>> updateMe(@AuthenticationPrincipal OAuth2UserInfo userInfo,
@RequestBody @Valid UserUpdateReqDto userUpdateReqDto) {
UserDto userDto = userService.updateUser(userInfo.getUserId(), userUpdateReqDto);
UserRespDto userRespDto = UserRespDto.of(userDto);
return ResponseEntity.ok(
ApiRespDto.<UserRespDto>builder()
.statusCode(StatusCode.OK.getCode())
.message(StatusCode.OK.getMessage())
.data(userRespDto)
.build()
);
}
}
12 changes: 9 additions & 3 deletions server/src/main/java/com/talkka/server/user/dao/UserEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,17 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Entity(name = "users")
@Getter
@Setter
@Builder
@NoArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@ToString
@EntityListeners(AuditingEntityListener.class)
Expand Down Expand Up @@ -70,6 +69,7 @@ public class UserEntity {
private LocalDateTime updatedAt;

@OneToMany(mappedBy = "writer")
@ToString.Exclude
private List<BusReviewEntity> busReviews;

@Override
Expand All @@ -86,4 +86,10 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hashCode(userId);
}

public void updateUser(String nickname) {
if (nickname != null && !nickname.isEmpty()) {
this.nickname = nickname;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Getter
@Builder
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class UserCreateDto {
private String name;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.talkka.server.user.dto;

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Getter
@Builder
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class UserCreateReqDto {
@NotNull
@Size(min = 2, max = 20, message = "닉네임은 2자 이상 20자 이하로 입력해주세요.")
@Pattern(regexp = "^[a-zA-Z0-9가-힣]*$", message = "닉네임은 영문 대소문자, 한글, 숫자로만 입력해주세요.")
private String nickname;
}
52 changes: 25 additions & 27 deletions server/src/main/java/com/talkka/server/user/dto/UserDto.java
Original file line number Diff line number Diff line change
@@ -1,58 +1,56 @@
package com.talkka.server.user.dto;

import java.time.LocalDateTime;
import java.util.List;
import java.util.ArrayList;

import com.talkka.server.review.dao.BusReviewEntity;
import com.talkka.server.user.dao.UserEntity;
import com.talkka.server.user.enums.Grade;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Getter
@Builder
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class UserDto {
private Long userId;
private String name;
private String email;
private String nickname;
private String oauthProvider;
private String accessToken;
private Grade grade;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private List<BusReviewEntity> busReviews;

public static UserDto of(UserEntity userEntity) {
return UserDto.builder()
.userId(userEntity.getUserId())
.nickname(userEntity.getNickname())
.oauthProvider(userEntity.getOauthProvider())
.accessToken(userEntity.getAccessToken())
.grade(userEntity.getGrade())
.createdAt(userEntity.getCreatedAt())
.updatedAt(userEntity.getUpdatedAt())
.busReviews(userEntity.getBusReviews())
.build();
return new UserDto(
userEntity.getUserId(),
userEntity.getName(),
userEntity.getEmail(),
userEntity.getNickname(),
userEntity.getOauthProvider(),
userEntity.getAccessToken(),
userEntity.getGrade(),
userEntity.getCreatedAt(),
userEntity.getUpdatedAt()
);
}

public UserEntity toEntity() {
return UserEntity.builder()
.userId(userId)
.nickname(nickname)
.oauthProvider(oauthProvider)
.accessToken(accessToken)
.grade(grade)
.createdAt(createdAt)
.updatedAt(updatedAt)
.busReviews(busReviews)
.build();
return new UserEntity(
userId,
name,
email,
nickname,
oauthProvider,
accessToken,
grade,
createdAt,
updatedAt,
new ArrayList<>());
}
}
Loading

0 comments on commit be296c7

Please sign in to comment.