본문 바로가기

Project

[Project] Lv_2 스케줄 프로젝트(심화)

 


 

요구 사항

유저를 생성, 조회, 수정, 삭제할 수 있습니다.

유저는 아래와 같은 필드를 가집니다.

  • ✅ 유저명, 이메일, 작성일 , 수정일 필드
  • ✅ 작성일, 수정일 필드는 JPA Auditing을 활용합니다.

연관관계 구현

  • ✅ 일정은 이제 작성 유저명 필드 대신 유저 고유 식별자 필드를 가집니다.

 


 

요구 구현

요구 사항의 추가에 따른 API 명세서, ERD 추가

더보기

 


 

 


 

 


 

 


 

 

 


 

요구 사항의 변경에 따른 API 명세서, ERD 수정

더보기

 


 

 


 

 


 

UserController

더보기
@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

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

    @PostMapping
    public ResponseEntity<CreateUserResponseDto> createUser(@RequestBody @Valid CreateUserRequestDto requestDto) {

        CreateUserResponseDto createUserResponseDto = userService.saveUser(requestDto);

        return new ResponseEntity<>(createUserResponseDto, HttpStatus.CREATED);
    }

    @GetMapping("/{id}")
    public ResponseEntity<FindUserResponseDto> findUser(@PathVariable Long id) {
        FindUserResponseDto userResponseDto = userService.findUserById(id);

        return new ResponseEntity<>(userResponseDto, HttpStatus.OK);
    }

    @PatchMapping("/{id}")
    public ResponseEntity<UpdateUserResponseDto> updateUser(
            @PathVariable Long id,
            @RequestBody UpdateUserRequestDto requestDto
    ) {
        UpdateUserResponseDto updateUserResponseDto = userService.updateUser(id, requestDto);

        return new ResponseEntity<>(updateUserResponseDto,HttpStatus.OK);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);

        return new ResponseEntity<>(HttpStatus.OK);
    }
}

 

 

UserService

더보기
public interface UserService {
    CreateUserResponseDto saveUser(CreateUserRequestDto requestDto);
    FindUserResponseDto findUserById(Long id);
    UpdateUserResponseDto updateUser(Long id, UpdateUserRequestDto requestDto);
    void deleteUser (Long id);
}

 

 

UserServiceImpl

더보기
@Service
public class UserServiceImpl implements UserService{

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public CreateUserResponseDto saveUser(CreateUserRequestDto requestDto) {

        User user = new User(requestDto.getUsername(), requestDto.getEmail());
        User savedUser = userRepository.save(user);

        return new CreateUserResponseDto(savedUser);
    }

    @Override
    public FindUserResponseDto findUserById(Long id) {
        User user = userRepository.findByIdOrElseThrow(id);

        return new FindUserResponseDto(user);
    }

    @Override
    public UpdateUserResponseDto updateUser(Long id, UpdateUserRequestDto requestDto) {
        User user = userRepository.findByIdOrElseThrow(id);

        if (requestDto.getUsername() != null) {
            user.updateUsername(requestDto.getUsername());
        }

        if (requestDto.getEmail() != null) {
            user.updateEmail(requestDto.getEmail());
        }

        return new UpdateUserResponseDto(user);
    }

    @Override
    public void deleteUser(Long id) {
        User user = userRepository.findByIdOrElseThrow(id);

        userRepository.delete(user);
    }
}

 

 

UserRepository

더보기
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    default User findByIdOrElseThrow(Long id) {
        return findById(id).orElseThrow(
                () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Does not exist id = " + id)
        );
    }
}

 

 

User

더보기
@Getter
@Entity
@Table(name = "user")
public class User extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String username;
    private String email;

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }

    public User() {

    }

    public void updateUsername(String username) {
        this.username = username;
    }

    public void updateEmail(String email) {
        this.email = email;
    }
}

 

 

CreateUserRequestDto

더보기
@Getter
@AllArgsConstructor
public class CreateUserRequestDto {
    @NotNull
    private final String username;
    @Email(message = "이메일 형식이 올바르지 않습니다")
    private final String email;
}

 

 

CreateUserResponseDto

더보기
@Getter
public class CreateUserResponseDto {
    private final Long id;
    private final String username;
    private final String email;
    private final Date createdAt;
    private final Date updatedAt;

    public CreateUserResponseDto(User user) {
        this.id = user.getId();
        this.username = user.getUsername();
        this.email = user.getEmail();
        this.createdAt = user.getCreatedAt();
        this.updatedAt = user.getUpdatedAt();
    }
}

 

 

FindUserResponseDto

더보기
@Getter
public class FindUserResponseDto {
    private final Long id;
    private final String username;
    private final String email;

    public FindUserResponseDto(User user) {
        this.id = user.getId();
        this.username = user.getUsername();
        this.email = user.getEmail();
    }
}

 

 

UpdateUserRequestDto

더보기
@Getter
@AllArgsConstructor
public class UpdateUserRequestDto {
    @NotNull
    private final String username;
    @Email(message = "잘못된 이메일 형식입니다.")
    private final String email;
}

 

 

UpdateUserResponseDto

더보기
@Getter
public class UpdateUserResponseDto {
    private final Long id;
    private final String name;
    private final String email;

    public UpdateUserResponseDto(User user) {
        this.id = user.getId();
        this.name = user.getUsername();
        this.email = user.getEmail();
    }
}

 

 


 

 

"✅ 일정은 이제 작성 유저명 필드 대신 유저 고유 식별자 필드를 가집니다."에 대한 코드 변경

더보기

CreateScheduleRequestDto

@Getter
@AllArgsConstructor
public class CreateScheduleRequestDto {
    @NotNull
    private Long userId;
    private String title;
    private String content;
    @NotNull
    private String password;
}
  • 변경 내용 : String username -> `Long userId`

 

 

CreateScheduleResponseDto

@Getter
public class CreateScheduleResponseDto {
    private final Long id;
    private final String title;
    private final Long userId;
    private final String content;
    private final Date createdAt;
    private final Date updatedAt;

    public CreateScheduleResponseDto(Schedule schedule) {
        this.id = schedule.getId();
        this.title = schedule.getTitle();
        this.userId = schedule.getUserId();
        this.content = schedule.getContent();
        this.createdAt = schedule.getCreatedAt();
        this.updatedAt = schedule.getUpdatedAt();
    }
}
  • 변경 내용 : String username -> `Long userId`

 

 

FindScheduleResponseDto

@Getter
public class FindScheduleResponseDto {
    private final Long id;
    private final String title;
    private final Long userId;
    private final String content;
    private final Date createdAt;
    private final Date updatedAt;

    public FindScheduleResponseDto(Schedule schedule) {
        this.id = schedule.getId();
        this.title = schedule.getTitle();
        this.userId = schedule.getUserId();
        this.content = schedule.getContent();
        this.createdAt = schedule.getCreatedAt();
        this.updatedAt = schedule.getUpdatedAt();
    }
}
  • 변경 내용 : String username -> `Long userId`
  • 변경 내용 : this.username = schedule.getUsername() -> `this.userId = schedule.getUserId()`

 

 

Schedule

@Entity
@Table(name = "schedule")
@Getter
public class Schedule extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private Long userId;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String content;

    @Column(nullable = false)
    private String password;

    public Schedule() {}

    public Schedule(Long userId, String title, String content, String password) {
        this.userId = userId;
        this.title = title;
        this.content = content;
        this.password = password;
    }

    public Schedule(Long id, Long userId, String title, String content) {
        this.id = id;
        this.userId = userId;
        this.title = title;
        this.content = content;
    }

	...
}
  • 변경 내용 : String username -> `Long userId`
  • 변경 내용 : this.username = username -> `this.userId = userId`

 

@Service
public class ScheduleServiceImpl implements ScheduleService{

    private final ScheduleRepository scheduleRepository;

    public ScheduleServiceImpl(ScheduleRepository scheduleRepository) {
        this.scheduleRepository = scheduleRepository;
    }

    public CreateScheduleResponseDto saveSchedule(CreateScheduleRequestDto requestDto) {
        if (requestDto.getUserId() == null || requestDto.getTitle() == null || requestDto.getContent() == null || requestDto.getPassword() == null) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "There is no content.");
        }

        Schedule schedule = new Schedule(requestDto.getUserId(), requestDto.getTitle(), requestDto.getContent(), requestDto.getPassword());
        Schedule savedSchedule = scheduleRepository.save(schedule);

        return new CreateScheduleResponseDto(savedSchedule);
    }
    
    ...
}
  • 변경 내용 : requestDto.getUsername -> `requestDto.getUserId()`

 

 


 

회고

전체적인 방식은 이전부터 계속 진행해왔던 방식이기 때문에 큰 어려움은 없었으나, 요구사항 추가 및 변경에 따른 API 명세서, ERD, 프로그래밍 코드를 추가 및 수정한는 것에 많은 시간이 소요되는 것을 느꼈습니다. 실무에서도 잦은 요구사항 변경 및 정해지지 않은 요구사항 등에 대한 변수가 있는데, 미리 경험해볼 수 있었던 시간이었습니다.