-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
๐จ ProblemService ๋ฌธ์ ์์ฑ ๋ฐ ๋ฌธ์ ์ ๋ฐ์ดํธ ์ํฐํฐ๋ช ์์ #11
base: develop
Are you sure you want to change the base?
Conversation
src/main/java/site/haruhana/www/dto/ProblemDto.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/dto/ProblemDto.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋
ํ์ธ์. ์๋์ด ๊ฐ๋ฐ์๋ก์ 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์์ ์์: package site.haruhana.www.dto;
import lombok.*;
import site.haruhana.www.entity.ProblemCategory;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder // Builder ํจํด ์ ์ฉ
public class ProblemDto {
private Long id;
@NotNull(message = "์ ๋ชฉ์ ํ์์
๋๋ค.")
private String title;
private String description;
@Min(value = 0, message = "๋ ๋ฒจ์ 0 ์ด์์ด์ด์ผ ํฉ๋๋ค.") // ์ ํจ์ฑ ๊ฒ์ฌ ์ถ๊ฐ
private int level; // int ๋ก ๋ณ๊ฒฝ
private ProblemCategory problemCategory; // Problem ์ข
๋ฅ (Enum)
} ์ ๋ฆฌ๋ทฐ๋ ํ์ฌ ์ฝ๋๋ง ๋ณด๊ณ ํ๋จํ ๋ด์ฉ์ด๋ฉฐ, ์ค์ ์ฌ์ฉ ์ํฉ์ ๋ฐ๋ผ ๋ ์ ์ ํ ๊ฐ์ ๋ฐฉ์์ด ์์ ์ ์์ต๋๋ค. ์ฝ๋ ๋ฆฌ๋ทฐ ๊ฒฐ๊ณผ๊ฐ ํ๋ก์ ํธ์ ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. ๊ถ๊ธํ ์ ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ง ์ง๋ฌธํด์ฃผ์ธ์. |
src/main/java/site/haruhana/www/repository/ProblemRepository.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/repository/ProblemRepository.java ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋ฐ์ ์ธ ํ๊ฐ: ์ฝ๋๋ ๋งค์ฐ ๊ฐ๋จํ๊ณ ๋ช
ํํฉ๋๋ค. 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฒฐ๋ก : ํ์ฌ ์ฝ๋๋ ๋น๊ต์ ์ ์์ฑ๋์์ง๋ง, ์์์ ์ธ๊ธํ ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ๊ณผ ๊ฐ์ ํฌ์ธํธ๋ฅผ ๊ณ ๋ คํ์ฌ ์ฝ๋๋ฅผ ๋์ฑ ์์ ์ ์ด๊ณ ํจ์จ์ ์ผ๋ก ๋ง๋ค ์ ์์ต๋๋ค. ํนํ, ์ค์ ์ฌ์ฉ๋๊ณผ ๋ฐ์ดํฐ์ ํน์ฑ์ ๊ณ ๋ คํ์ฌ ์ฑ๋ฅ ๋ฐ ๋ณด์ ๊ด๋ จ ์ด์๋ฅผ ํด๊ฒฐํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ํ ์คํธ ์ฝ๋ ์์ฑ์ ํตํด ์ฝ๋์ ์์ ์ฑ์ ํ๋ณดํ๋ ๊ฒ๋ ์์ง ๋ง์ญ์์ค. |
src/main/java/site/haruhana/www/Service/ProblemService.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/Service/ProblemService.java ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋ฐ์ ์ธ ํ๊ฐ: ์ฝ๋๋ ๊ธฐ๋ณธ์ ์ธ CRUD ๋ก์ง์ ์ ๊ตฌํํ๊ณ ์์ผ๋ฉฐ, ๊ฐ๋
์ฑ๋ ๋์์ง ์์ต๋๋ค. 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
์์ ์์ ( @Transactional
public Problem updateProblem(Long id, Problem updatedProblem) {
Problem problem = problemRepository.findById(id)
.orElseThrow(() -> new NoSuchElementException("Problem not found"));
// ํ๋๊ฐ null์ด ์๋ ๊ฒฝ์ฐ์๋ง ์
๋ฐ์ดํธ
if (updatedProblem.getTitle() != null) {
problem.setTitle(updatedProblem.getTitle());
}
if (updatedProblem.getDescription() != null) {
problem.setDescription(updatedProblem.getDescription());
}
if (updatedProblem.getLevel() != null) {
problem.setLevel(updatedProblem.getLevel());
}
if (updatedProblem.getProblemCategory() != null) {
problem.setProblemCategory(updatedProblem.getProblemCategory());
}
// problemRepository.save(problem); // JPA๊ฐ ์๋ ์ ์ฅํ๋ฏ๋ก ๋ถํ์
return problem;
} ๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ์ ์์ฑ๋์์ง๋ง, ์์ ์ธ๊ธ๋ ๊ฐ์ ์ฌํญ๋ค์ ์ ์ฉํ๋ฉด ์ฝ๋ ํ์ง, ์ฑ๋ฅ, ๋ณด์์ ๋์ฑ ํฅ์์ํฌ ์ ์์ต๋๋ค. ํนํ, ์ ํจ์ฑ ๊ฒ์ฌ, ๊ถํ ๊ฒ์ฌ, DTO ์ฌ์ฉ ์ ๋ต, ์์ธ ์ฒ๋ฆฌ, ์ฑ๋ฅ ๊ฐ์ ์ ์ง์คํ๋ ๊ฒ์ด ์ข์ต๋๋ค. |
src/main/java/site/haruhana/www/controller/ProblemController.java ๋ฆฌ๋ทฐProblemController.java ์ฝ๋ ๋ฆฌ๋ทฐ๋ค์์ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ ๋ฐ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์ด ๋ฆฌ๋ทฐ๋ฅผ ํตํด ์ฝ๋์ ํ์ง์ ๊ฐ์ ํ๊ณ ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๋ ๋ฐ ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. |
src/main/java/site/haruhana/www/repository/ProblemRepository.java ๋ฆฌ๋ทฐProblemRepository.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋
ํ์ธ์. ์๋์ด ๊ฐ๋ฐ์๋ก์ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ์ ์์ฑ๋์ด ์์ง๋ง, null ์ฒ๋ฆฌ, N+1 ๋ฌธ์ , ํ์ด์ง ์ฑ๋ฅ, ๋ณด์ ๋ฑ์ ์ธก๋ฉด์์ ๊ฐ์ ์ ์ฌ์ง๊ฐ ์์ต๋๋ค. ์ ์๋ ๊ฐ์ ์ฌํญ๋ค์ ์ ์ฉํ์ฌ ์ฝ๋ ํ์ง๊ณผ ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์์ต๋๋ค. ๋ํ, ์ค์ ์๋น์ค ํ๊ฒฝ์ ๋ง๊ฒ ๋ณด์ ๋ฐ ์ ๊ทผ ์ ์ด๋ฅผ ๊ฐํํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. |
src/main/java/site/haruhana/www/dto/ProblemDto.java ๋ฆฌ๋ทฐProblemDto.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋
ํ์ธ์. ์๋์ด ๊ฐ๋ฐ์๋ก์
์ฝ๋: package site.haruhana.www.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import site.haruhana.www.entity.ProblemCategory;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ProblemDto {
private Long id;
private String title;
private String description;
private Integer level;
private ProblemCategory problemCategory;
} 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ ๋ฐ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์์ ์์: package site.haruhana.www.dto;
import lombok.*;
import site.haruhana.www.entity.ProblemCategory;
import javax.validation.constraints.NotNull;
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode
public class ProblemDto {
/**
* ๋ฌธ์ ID
*/
private Long id;
/**
* ๋ฌธ์ ์ ๋ชฉ
*/
@NotNull
private String title;
/**
* ๋ฌธ์ ์ค๋ช
*/
private String description;
/**
* ๋ฌธ์ ๋ ๋ฒจ (1-5)
*/
private Integer level;
/**
* ๋ฌธ์ ์นดํ
๊ณ ๋ฆฌ (์: ์๊ณ ๋ฆฌ์ฆ, ์๋ฃ๊ตฌ์กฐ)
*/
private ProblemCategory problemCategory;
} ๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ๊ฐ๊ฒฐํ๊ณ ๊ฐ๋ ์ฑ์ด ์ข์ต๋๋ค. ํ์ง๋ง ๋ช ๊ฐ์ง ๊ฐ์ ์ ์ ํตํด ์ฝ๋์ ์์ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ๋์ฑ ํฅ์์ํฌ ์ ์์ต๋๋ค. ์์์ ์ธ๊ธํ ์ ์๋ค์ ๊ณ ๋ คํ์ฌ ์ฝ๋๋ฅผ ๊ฐ์ ํ๋ ๊ฒ์ ์ถ์ฒํฉ๋๋ค. ๊ถ๊ธํ ์ ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ง ์ง๋ฌธํด์ฃผ์ธ์. |
src/main/java/site/haruhana/www/Service/ProblemService.java ๋ฆฌ๋ทฐProblemService ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋ฐ์ ์ธ ํ๊ฐ: ์ฝ๋๋ ๋น๊ต์ ๊น๋ํ๊ณ ๊ฐ๋
์ฑ์ด ์ข์ต๋๋ค. Lombok์ ์ฌ์ฉํ์ฌ boilerplate ์ฝ๋๋ฅผ ์ค์ด๊ณ , ๋ก๊น
์ ํตํด ๋์์ ์ถ์ ํ๊ธฐ ์ฉ์ดํ๊ฒ ๋ง๋ค์์ต๋๋ค. 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
// DTO ์์
@Data
public class UpdateProblemRequest {
private String title;
private String description;
private Integer level;
private ProblemCategory problemCategory;
}
// Service ๋ฉ์๋ ์์
@Transactional
public Problem updateProblem(Long id, UpdateProblemRequest request) {
log.info("Updating problem with ID: {}", id);
Problem problem = problemRepository.findById(id)
.orElseThrow(() -> new NoSuchElementException("Problem not found"));
problem.updateProblem(
request.getTitle(),
request.getDescription(),
request.getLevel(),
request.getProblemCategory()
);
return problem; // save ๋ถํ์
}
๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ์ ์์ฑ๋์์ง๋ง, ์์ ์ ์๋ ๊ฐ์ ์ฌํญ๋ค์ ์ ์ฉํ๋ฉด ์ฝ๋ ํ์ง, ์ฑ๋ฅ, ๋ณด์์ฑ์ ๋์ฑ ํฅ์์ํฌ ์ ์์ต๋๋ค. ํนํ ์์ธ ์ฒ๋ฆฌ, N+1 ๋ฌธ์ , ๊ถํ ๊ฒ์ฌ, ์ ๋ ฅ ์ ํจ์ฑ ๊ฒ์ฌ ๋ฑ์ ์ค์ ์๋น์ค ์ด์์์ ์ค์ํ ๋ถ๋ถ์ด๋ ๋ฐ๋์ ๊ณ ๋ คํด์ผ ํฉ๋๋ค. |
src/main/java/site/haruhana/www/controller/ProblemController.java ๋ฆฌ๋ทฐProblemController.java ์ฝ๋ ๋ฆฌ๋ทฐ๋ค์์ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ ๋ฐ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์์ ๋ ์ฝ๋ ์์ (์ผ๋ถ): @RestController
@RequiredArgsConstructor
@RequestMapping("/problems")
public class ProblemController {
private final ProblemService problemService;
@GetMapping
public ResponseEntity<Page<ProblemDto>> getAllProblems(@PageableDefault Pageable pageable) {
Page<ProblemDto> problems = problemService.getAllProblems(pageable);
return ResponseEntity.ok(problems);
}
@GetMapping("/{id}")
public ResponseEntity<ProblemDto> getProblemById(@PathVariable Long id) {
try {
ProblemDto problemDto = problemService.getProblemById(id);
return ResponseEntity.ok(problemDto);
} catch (NoSuchElementException e) {
return ResponseEntity.notFound().build();
}
}
@PostMapping
public ResponseEntity<ProblemDto> createProblem(@Valid @RequestBody ProblemDto problemDto) {
ProblemDto createdProblem = problemService.createProblem(problemDto);
return new ResponseEntity<>(createdProblem, HttpStatus.CREATED);
}
@PutMapping("/{id}")
public ResponseEntity<ProblemDto> updateProblem(@PathVariable Long id, @Valid @RequestBody ProblemDto problemDto) {
try {
ProblemDto updatedProblem = problemService.updateProblem(id, problemDto);
return ResponseEntity.ok(updatedProblem);
} catch (NoSuchElementException e) {
return ResponseEntity.notFound().build();
}
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProblem(@PathVariable Long id) {
problemService.deleteProblem(id);
return ResponseEntity.noContent().build();
}
@GetMapping("/category")
public ResponseEntity<Page<ProblemDto>> getProblemsByCategory(
@RequestParam("category") ProblemCategory category,
@PageableDefault Pageable pageable) {
Page<ProblemDto> problems = problemService.getProblemsByCategory(category, pageable);
if (problems.isEmpty()) {
return ResponseEntity.noContent().build();
}
return ResponseEntity.ok(problems);
}
} ์ด ๋ฆฌ๋ทฐ๋ ์ฝ๋์ ํ์ฌ ์ํ์ ๋ํ ๋ถ์์ด๋ฉฐ, ์ค์ ์๋น์ค ํ๊ฒฝ์์์ ๋์์ ์์๊ณผ ๋ค๋ฅผ ์ ์์ต๋๋ค. ์ถฉ๋ถํ ํ ์คํธ์ ๊ฒ์ฆ์ ๊ฑฐ์ณ ์์ ์ ์ธ ์๋น์ค๋ฅผ ๊ตฌ์ถํ์๊ธฐ ๋ฐ๋๋๋ค. |
src/main/java/site/haruhana/www/exception/ProblemNotFoundException.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/exception/ProblemNotFoundException.java ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋ฐ์ ์ธ ํ๊ฐ: ์ ๊ณต๋ ์ฝ๋๋ ๊ฐ๋จํ๊ณ ๋ช ํํ ์ฌ์ฉ์ ์ ์ ์์ธ ํด๋์ค์ ๋๋ค. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ์ ๋งค์ฐ ์ข์ต๋๋ค. ํ์ง๋ง, ์กฐ๊ธ ๋ ๊ฐ์ ํ ๋ถ๋ถ์ด ์์ต๋๋ค. 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฒฐ๋ก : ์ ๊ณต๋ ์ฝ๋๋ ๊ธฐ๋ณธ์ ์ธ ์์ธ ํด๋์ค๋ก์ ์ ์์ฑ๋์์ต๋๋ค. ํ์ง๋ง, ๋ฌธ์ ํด๊ฒฐ ๋ฐ ๋๋ฒ๊น ์ ๋๊ธฐ ์ํด ๋ฌธ์ ID๋ฅผ ํฌํจํ๊ณ ์์ธ ๋ฉ์์ง๋ฅผ ๊ตฌ์ฒดํํ๋ ๋ฑ์ ๊ฐ์ ์ ๊ณ ๋ คํด๋ณผ ์ ์์ต๋๋ค. ๋ํ, ์์ธ์ ์ค์๋๋ฅผ ๊ณ ๋ คํ์ฌ checked exception์ผ๋ก ๋ณ๊ฒฝํ๋ ๊ฒ์ ๊ฒํ ํ ์ ์์ต๋๋ค. |
src/main/java/site/haruhana/www/repository/ProblemRepository.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/repository/ProblemRepository.java ์ฝ๋ ๋ฆฌ๋ทฐ1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฒฐ๋ก :
|
src/main/java/site/haruhana/www/controller/GlobalExceptionHandler.java ๋ฆฌ๋ทฐGlobalExceptionHandler.java ์ฝ๋ ๋ฆฌ๋ทฐ1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
์์ ๋ ์ฝ๋ ์์ (์ผ๋ถ): package site.haruhana.www.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import site.haruhana.www.exception.ProblemNotFoundException;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(ProblemNotFoundException.class)
public ResponseEntity<ErrorResponse> handleProblemNotFoundException(ProblemNotFoundException ex) {
logger.warn("Problem not found: {}", ex.getMessage());
ErrorResponse response = new ErrorResponse("RESOURCE_NOT_FOUND", "์์ฒญํ์ ์ ๋ณด๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.");
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
logger.error("Unexpected error: {}", ex.getMessage(), ex); // ์คํ ํธ๋ ์ด์ค ๋ก๊น
ErrorResponse response = new ErrorResponse("INTERNAL_SERVER_ERROR", "์๋ฒ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// ErrorResponse ํด๋์ค (์์)
class ErrorResponse {
private String code;
private String message;
public ErrorResponse(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
} ๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ข์ ์ฝ๋์ด์ง๋ง, ๋ณด์๊ณผ ์ ์ง๋ณด์์ฑ์ ์ํด ๋ช ๊ฐ์ง ๊ฐ์ ์ฌํญ์ ์ ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ํนํ, ์์ธ ๋ฉ์์ง ๋ ธ์ถ ๋ฌธ์ ๋ ๋ฐ๋์ ํด๊ฒฐํด์ผ ํฉ๋๋ค. ์์ ์ ์๋ ๊ฐ์ ์ฌํญ๋ค์ ์ ์ฉํ๋ฉด ๋์ฑ ์์ ์ ์ด๊ณ ์์ ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ ์ ์์ ๊ฒ์ ๋๋ค. |
src/main/java/site/haruhana/www/dto/ProblemDto.java ๋ฆฌ๋ทฐProblemDto.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋
ํ์ธ์. ์๋์ด ๊ฐ๋ฐ์๋ก์ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ์ ์์ฑ๋์์ง๋ง, ์์์ ์ธ๊ธํ ๋ฌธ์ ์ ๋ค์ ์์ ํ๊ณ ๊ฐ์ ์ ์์ ์ ์ฉํ๋ฉด ๋์ฑ ์์ ํ๊ณ ํจ์จ์ ์ธ ์ฝ๋๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ํนํ ์์ฑ์ ์ค๋ฅ ์์ ๊ณผ ๋ฏผ๊ฐ ์ ๋ณด ์ฒ๋ฆฌ ๋ถ๋ถ์ ์ฃผ์๋ฅผ ๊ธฐ์ธ์ฌ์ผ ํฉ๋๋ค. ๋ํ, ์ ํจ์ฑ ๊ฒ์ฌ ๋ฐ null ์ฒ๋ฆฌ๋ฅผ ํตํด ๋์ฑ robustํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค ์ ์์ต๋๋ค. ์ด ๋ฆฌ๋ทฐ๊ฐ ์ฝ๋ ๊ฐ์ ์ ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. ํน์ ๋ ๊ถ๊ธํ ์ ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ง ์ง๋ฌธํด์ฃผ์ธ์. |
src/main/java/site/haruhana/www/entity/Problem.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/entity/Problem.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋
ํ์ธ์. ์๋์ด ๊ฐ๋ฐ์๋ก์ ์ ๊ณตํด์ฃผ์ ๋ฆฌ๋ทฐ ๋์ ์ฝ๋: +import lombok.Builder;
+import site.haruhana.www.dto.ProblemDto;
+@Builder
+ * @param updatedProblemDto ๊ฐฑ์ ํ ๋ฌธ์ ์ ๋ณด๋ฅผ ํฌํจํ๋ DTO
+ public void update(ProblemDto updatedProblemDto) {
+ this.title = updatedProblemDto.getTitle();
+ this.description = updatedProblemDto.getDescription();
+ this.level = updatedProblemDto.getLevel();
+ this.problemCategory = updatedProblemDto.getProblemCategory();
+ } 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์ฃผ์:
์ ์ฒด ์ฝ๋๋ฅผ ์ ๊ณตํด์ฃผ์๋ฉด ๋์ฑ ์์ธํ๊ณ ์ ํํ ๋ฆฌ๋ทฐ๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค. ์๊ณ ํ์ธ์! |
src/test/java/site/haruhana/www/Service/ProblemServiceTest.java ๋ฆฌ๋ทฐsrc/test/java/site/haruhana/www/Service/ProblemServiceTest.java ์ฝ๋ ๋ฆฌ๋ทฐ๋ค์์ ์ฃผ์ด์ง 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์์ฝ ์ ๋ฐ์ ์ผ๋ก ์ ์์ฑ๋ ํ
์คํธ ์ฝ๋์
๋๋ค. ์ ์๋ ๊ฐ์ ์ฌํญ๋ค์ ์ ์ฉํ๋ฉด ์ฝ๋์ ์์ ์ฑ๊ณผ ์ ๋ขฐ์ฑ์ ๋์ฑ ๋์ผ ์ ์์ต๋๋ค. ํนํ |
src/main/java/site/haruhana/www/controller/ProblemController.java ๋ฆฌ๋ทฐ์ฝ๋ ๋ฆฌ๋ทฐ: src/main/java/site/haruhana/www/controller/ProblemController.java์ดํ: ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ๊น๋ํ๊ณ ๊ฐ๋ ์ฑ์ด ์ข์ต๋๋ค. Lombok์ ์ฌ์ฉํ์ฌ boilerplate ์ฝ๋๋ฅผ ์ค์ด๊ณ , Spring MVC์ ํ์ค์ ์ธ ๋ฐฉ์์ ์ ๋ฐ๋ฅด๊ณ ์์ต๋๋ค. ์์ธ ์ฒ๋ฆฌ, ์๋ต ํฌ๋งท, API ์๋ํฌ์ธํธ ์ค๊ณ ๋ฑ ๊ธฐ๋ณธ์ ์ธ ์ฌํญ๋ค์ด ์ ๊ตฌํ๋์ด ์์ต๋๋ค. ํ์ง๋ง ๋ช ๊ฐ์ง ๊ฐ์ ์ ๊ณผ ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ๋ค์ด ์กด์ฌํฉ๋๋ค. 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ ๋ฐ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
์ด ๋ฆฌ๋ทฐ๋ฅผ ๋ฐํ์ผ๋ก ์ฝ๋๋ฅผ ๊ฐ์ ํ์ฌ ๋์ฑ ์์ ์ ์ด๊ณ ํจ์จ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ์๊ธธ ๋ฐ๋๋๋ค. |
src/main/java/site/haruhana/www/Service/ProblemService.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/Service/ProblemService.java ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ๊น๋ํ๊ณ ๊ฐ๋
์ฑ์ด ์ข์ต๋๋ค. 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฐ์ ๋ public void deleteProblem(Long id) {
log.info("๋ฌธ์ ์ญ์ ์์ - ๋ฌธ์ ID: {}", id);
try {
problemRepository.deleteById(id);
log.info("๋ฌธ์ ์ญ์ ์๋ฃ - ๋ฌธ์ ID: {}", id);
} catch (EmptyResultDataAccessException e) {
log.error("๋ฌธ์ ์ญ์ ์คํจ - ๋ฌธ์ ๋ฅผ ์ฐพ์ ์ ์์ (ID: {})", id);
throw new ProblemNotFoundException("์ญ์ ํ ๋ฌธ์ ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.");
} catch (Exception e) {
log.error("๋ฌธ์ ์ญ์ ์ค ์ค๋ฅ ๋ฐ์ - ๋ฌธ์ ID: {}, ์ค๋ฅ: {}", id, e.getMessage());
throw new RuntimeException("๋ฌธ์ ์ญ์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.", e);
}
} ๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ ์์ฑ๋ ์ฝ๋์ด์ง๋ง, ์์์ ์ธ๊ธํ ๊ฐ์ ์ฌํญ๋ค์ ์ ์ฉํ๋ฉด ์ฝ๋์ ํ์ง, ์ฑ๋ฅ, ๋ณด์์ฑ์ ๋์ฑ ํฅ์์ํฌ ์ ์์ต๋๋ค. ํนํ, N+1 ๋ฌธ์ ํด๊ฒฐ, ๊ถํ ๊ฒ์ฌ, ์ ๋ ฅ๊ฐ ๊ฒ์ฆ์ ์ค์ํ ๋ถ๋ถ์ด๋ ๋ฐ๋์ ๊ณ ๋ คํด์ผ ํฉ๋๋ค. |
src/main/java/site/haruhana/www/dto/ProblemDto.java ๋ฆฌ๋ทฐProblemDto.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋
ํ์ธ์. ์๋์ด ๊ฐ๋ฐ์์
๋๋ค. 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ ๋๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ ์์ฑ๋ ์ฝ๋์ด์ง๋ง, ์์ ์ธ๊ธ๋ ๋ช ๊ฐ์ง ๋ฌธ์ ์ ์ ์์ ํ๋ฉด ๋์ฑ ์์ ์ ์ด๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ด ์ฝ๋๊ฐ ๋ ๊ฒ์
๋๋ค. ํนํ ์์ฑ์ ์์ ์ ๋ฐ๋์ ํ์ํฉ๋๋ค. ๋ํ ๋ณด์์ ์ธ ์ธก๋ฉด๊ณผ ์ ์ฌ์ ์ธ |
src/main/java/site/haruhana/www/exception/ProblemNotFoundException.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/exception/ProblemNotFoundException.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋์ด ๊ฐ๋ฐ์๋ก์ ์ ์๋ ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๊ฒํ ํ๊ณ ๋ฆฌ๋ทฐํฉ๋๋ค. 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฒฐ๋ก : ์ฝ๋๋ ์ ๋ฐ์ ์ผ๋ก ํ๋ฅญํ๋ฉฐ, ๊ฐ๋ ์ฑ๋ ์ข์ต๋๋ค. ์์์ ์ ์๋ ๊ฐ์ ์ฌํญ๋ค์ ์ํฉ์ ๋ฐ๋ผ ์ ์ฉ ์ฌ๋ถ๋ฅผ ํ๋จํ๋ฉด ๋ฉ๋๋ค. ํนํ ์์ธ ๋ฉ์์ง๋ฅผ ๊ตฌ์ฒดํํ๋ ๊ฒ์ ๋๋ฒ๊น ์ ํฐ ๋์์ด ๋ ๊ฒ์ ๋๋ค. Checked Exception์ผ๋ก ๋ณ๊ฒฝํ ์ง๋ ๋น์ฆ๋์ค ์๊ตฌ์ฌํญ์ ๊ณ ๋ คํ์ฌ ๊ฒฐ์ ํด์ผ ํฉ๋๋ค. |
src/main/java/site/haruhana/www/converter/ProblemConverter.java ๋ฆฌ๋ทฐProblemConverter.java ์ฝ๋ ๋ฆฌ๋ทฐ1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์์ (NullPointerException ๋ฐฉ์ง): public Problem toEntity(ProblemDto dto) {
return Problem.builder()
.title(dto.getTitle() != null ? dto.getTitle() : "") // null ์ฒ๋ฆฌ
.description(dto.getDescription() != null ? dto.getDescription() : "") // null ์ฒ๋ฆฌ
.level(dto.getLevel() != null ? dto.getLevel() : 0) // null ์ฒ๋ฆฌ
.answer(dto.getAnswer() != null ? dto.getAnswer() : "") // null ์ฒ๋ฆฌ
.problemCategory(dto.getProblemCategory() != null ? dto.getProblemCategory() : "") // null ์ฒ๋ฆฌ
.build();
} ์์ (์ฃผ์ ์ถ๊ฐ): /**
* ProblemDto ๊ฐ์ฒด๋ฅผ Problem ์ํฐํฐ๋ก ๋ณํํฉ๋๋ค.
*
* @param dto ProblemDto ๊ฐ์ฒด
* @return Problem ์ํฐํฐ
*/
public Problem toEntity(ProblemDto dto) {
// ...
}
/**
* Problem ์ํฐํฐ๋ฅผ ProblemDto ๊ฐ์ฒด๋ก ๋ณํํฉ๋๋ค.
*
* @param entity Problem ์ํฐํฐ
* @return ProblemDto ๊ฐ์ฒด
*/
public ProblemDto toDto(Problem entity) {
// ...
} ๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ์ ์์ฑ๋์์ง๋ง, ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ์ ๋ฐฉ์งํ๊ณ ์ ์ง๋ณด์์ฑ์ ๋์ด๊ธฐ ์ํด ์์์ ์ ์๋ ๊ฐ์ ์ฌํญ๋ค์ ๊ณ ๋ คํด๋ณด๋ ๊ฒ์ด ์ข์ต๋๋ค. ํนํ NullPointerException ๋ฐฉ์ง์ ์ ๋ ฅ๊ฐ ๊ฒ์ฆ์ ์ค์ํ ๋ถ๋ถ์ ๋๋ค. |
src/main/java/site/haruhana/www/repository/ProblemRepository.java ๋ฆฌ๋ทฐProblemRepository.java ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋ฐ์ ์ธ ํ๊ฐ: ์ ๊ณต๋ ์ฝ๋๋ Spring Data JPA๋ฅผ ์ฌ์ฉํ์ฌ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฒฐ๋ก : ์ ๊ณต๋ ์ฝ๋๋ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฅ์ ์ ์ํํ๊ณ ์์ง๋ง, ์ค์ ์๋น์ค ํ๊ฒฝ์์๋ ๋ค์ํ ๊ฒ์ ์กฐ๊ฑด, ์ฑ๋ฅ ๋ฌธ์ , ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ๋ฌธ์ ๋ฑ์ ๊ณ ๋ คํ์ฌ ์ถ๊ฐ์ ์ธ ๊ฐ์ ์ด ํ์ํฉ๋๋ค. ์์์ ์ ์๋ ๊ฐ์ ์ ์๋ค์ ๋ฐํ์ผ๋ก ์๋น์ค ์๊ตฌ์ฌํญ์ ๋ง๊ฒ ์ฝ๋๋ฅผ ๊ฐ์ ํด ๋๊ฐ๋ฉด ๋์ฑ ๊ฒฌ๊ณ ํ๊ณ ํจ์จ์ ์ธ ์ฝ๋๋ฅผ ๋ง๋ค ์ ์์ ๊ฒ์ ๋๋ค. |
src/main/java/site/haruhana/www/controller/GlobalExceptionHandler.java ๋ฆฌ๋ทฐGlobalExceptionHandler.java ์ฝ๋ ๋ฆฌ๋ทฐ (์๋์ด ๊ฐ๋ฐ์ ๊ด์ )์ ๋ฐ์ ์ธ ํ๊ฐ: ์ฝ๋๋ ๋งค์ฐ ๊ฐ๋จํ๊ณ ์ง๊ด์ ์
๋๋ค. 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ ๋ฐ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฐ์ ์์ ์ฝ๋ (์ผ๋ถ): package site.haruhana.www.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import site.haruhana.www.dto.ErrorResponse; // ErrorResponse DTO ํ์
import site.haruhana.www.exception.ProblemNotFoundException;
import site.haruhana.www.exception.CustomBadRequestException; // ์์
import java.time.LocalDateTime;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(ProblemNotFoundException.class)
public ResponseEntity<ErrorResponse> handleProblemNotFoundException(ProblemNotFoundException ex) {
logger.warn("Problem not found: {}", ex.getMessage(), ex);
ErrorResponse errorResponse = new ErrorResponse("PROBLEM_NOT_FOUND", ex.getMessage(), LocalDateTime.now());
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(CustomBadRequestException.class) // ์์
public ResponseEntity<ErrorResponse> handleBadRequestException(CustomBadRequestException ex) {
logger.error("Bad Request: {}", ex.getMessage(), ex); // error ๋ ๋ฒจ๋ก ๋ก๊น
ErrorResponse errorResponse = new ErrorResponse("BAD_REQUEST", ex.getMessage(), LocalDateTime.now());
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
logger.error("Unexpected error occurred: {}", ex.getMessage(), ex); // ์๋ฌ ๋ก๊น
// ์ฌ์ฉ์์๊ฒ ์์ ํ ์ค๋ฅ ๋ฉ์์ง ์ ๊ณต
String errorMessage = "An unexpected error occurred. Please try again later.";
ErrorResponse errorResponse = new ErrorResponse("INTERNAL_SERVER_ERROR", errorMessage, LocalDateTime.now());
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
} ErrorResponse DTO ์์: package site.haruhana.www.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.time.LocalDateTime;
@Getter
@AllArgsConstructor
public class ErrorResponse {
private String errorCode;
private String message;
private LocalDateTime timestamp;
} ๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ๊ธฐ๋ณธ์ ์ธ ์์ธ ์ฒ๋ฆฌ๋ฅผ ์ ์ํํ๊ณ ์์ง๋ง, ๋ณด์, ๋ก๊น , ์ผ๋ฐ์ ์ธ ์์ธ ์ฒ๋ฆฌ ๋ฐฉ์ ๋ฑ์์ ๊ฐ์ ์ด ํ์ํฉ๋๋ค. ์์์ ์ ์๋ ์ ์๋ค์ ํตํด ์ฝ๋๋ฅผ ๋์ฑ robustํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฝ๋๋ก ๋ง๋ค ์ ์์ต๋๋ค. |
src/main/java/site/haruhana/www/Service/ProblemService.java ๋ฆฌ๋ทฐProblemService.java ์ฝ๋ ๋ฆฌ๋ทฐ๋ค์์ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ ๋ฐ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ์ ์์ฑ๋์์ง๋ง, ๋ช ๊ฐ์ง ๊ฐ์ ์ ์ ํตํด ์ฝ๋์ ํ์ง, ์ฑ๋ฅ, ๋ณด์์ ๋์ฑ ํฅ์์ํฌ ์ ์์ต๋๋ค. ์ ์ ์ ์ฌํญ๋ค์ ์ ์ฉํ์ฌ ์ฝ๋๋ฅผ ๊ฐ์ ํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค. |
src/test/java/site/haruhana/www/Service/ProblemServiceTest.java ๋ฆฌ๋ทฐsrc/test/java/site/haruhana/www/Service/ProblemServiceTest.java ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋ฐ์ ์ผ๋ก ํ ์คํธ ์ฝ๋๋ ์ ์์ฑ๋์์ผ๋ฉฐ, Mockito๋ฅผ ํ์ฉํ์ฌ Service Layer๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํ ์คํธํ๊ณ ์์ต๋๋ค. ๊ฐ ํ ์คํธ ์ผ์ด์ค๋ณ๋ก Given-When-Then ํจํด์ ์ ์ง์ผ ๊ฐ๋ ์ฑ๋ ์ข์ต๋๋ค. ํ์ง๋ง ๋ช ๊ฐ์ง ๊ฐ์ ์ ๊ณผ ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ์ ๋ฐ๊ฒฌํ์ต๋๋ค. 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก ์ ๋ฐ์ ์ผ๋ก ์ ์์ฑ๋ ํ ์คํธ ์ฝ๋์ด์ง๋ง, ์์์ ์ ์๋ ๊ฐ์ ์ฌํญ๋ค์ ์ ์ฉํ๋ฉด ํ ์คํธ์ ์ ๋ขฐ๋๋ฅผ ๋์ด๊ณ , ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ฅผ ์ฌ์ ์ ์๋ฐฉํ ์ ์์ต๋๋ค. ํนํ, ํ ์คํธ ์ฝ๋์์ Assertion์ ๊ฐํํ๊ณ , ๋ค์ํ ์์ธ ์ผ์ด์ค์ ๋ํ ํ ์คํธ๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. |
src/main/java/site/haruhana/www/controller/ProblemController.java ๋ฆฌ๋ทฐProblemController.java ์ฝ๋ ๋ฆฌ๋ทฐ๋ค์์ 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์์ ์์ (์ผ๋ถ): @RestController
@RequiredArgsConstructor
@RequestMapping("/api/problems")
public class ProblemController {
private final ProblemService problemService;
@GetMapping
public BaseResponse<Page<ProblemDto>> getAllProblems(Pageable pageable) {
Page<ProblemDto> problems = problemService.getAllProblems(pageable);
return problems.isEmpty()
? BaseResponse.onSuccess("์กฐํ๋ ๋ฌธ์ ๊ฐ ์์ต๋๋ค", problems)
: BaseResponse.onSuccess("๋ฌธ์ ๋ชฉ๋ก ์กฐํ์ ์ฑ๊ณตํ์ต๋๋ค", problems);
}
@GetMapping("/{problemId}")
public BaseResponse<ProblemDto> getProblemById(@PathVariable Long problemId) {
try {
ProblemDto problem = problemService.getProblemById(problemId);
return BaseResponse.onSuccess("๋ฌธ์ ์กฐํ์ ์ฑ๊ณตํ์ต๋๋ค", problem);
} catch (ProblemNotFoundException e) {
return BaseResponse.onNotFound("๋ฌธ์ ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค");
}
}
@PostMapping
public BaseResponse<ProblemDto> createProblem(@Valid @RequestBody ProblemDto problemDto) { // @Valid ์ถ๊ฐ
try {
ProblemDto savedProblem = problemService.createProblem(problemDto);
return BaseResponse.onCreate("๋ฌธ์ ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์์ฑ๋์์ต๋๋ค", savedProblem);
} catch (IllegalArgumentException e) { // ๊ตฌ์ฒด์ ์ธ ์์ธ ์ฒ๋ฆฌ
return BaseResponse.onBadRequest("๋ฌธ์ ์์ฑ์ ์คํจํ์ต๋๋ค: " + e.getMessage());
} catch (Exception e) {
return BaseResponse.onBadRequest("๋ฌธ์ ์์ฑ์ ์คํจํ์ต๋๋ค: " + e.getMessage());
}
}
@PutMapping("/{problemId}") //PathVariable ์ด๋ฆ ๋ณ๊ฒฝ
public BaseResponse<ProblemDto> updateProblem(@PathVariable Long problemId, @Valid @RequestBody ProblemDto problemDto) { // @Valid ์ถ๊ฐ
try {
ProblemDto updatedProblem = problemService.updateProblem(problemId, problemDto);
return BaseResponse.onSuccess("๋ฌธ์ ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์์ ๋์์ต๋๋ค", updatedProblem);
} catch (ProblemNotFoundException e) {
return BaseResponse.onNotFound("์์ ํ ๋ฌธ์ ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค");
}
}
@DeleteMapping("/{problemId}") //PathVariable ์ด๋ฆ ๋ณ๊ฒฝ
public BaseResponse<Void> deleteProblem(@PathVariable Long problemId) {
try {
problemService.deleteProblem(problemId);
return BaseResponse.onSuccess("๋ฌธ์ ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์ญ์ ๋์์ต๋๋ค", null);
} catch (ProblemNotFoundException e) {
return BaseResponse.onNotFound("์ญ์ ํ ๋ฌธ์ ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค");
}
}
@GetMapping("/category")
public BaseResponse<Page<ProblemDto>> getProblemsByCategory( // ๋ฐํ ํ์
๋ณ๊ฒฝ
@RequestParam("category") ProblemCategory category,
@PageableDefault Pageable pageable) {
Page<ProblemDto> problemPage = problemService.getProblemsByCategory(category, pageable);
if (problemPage.isEmpty()) {
return BaseResponse.onSuccess(
String.format("%s ์นดํ
๊ณ ๋ฆฌ์ ๋ฌธ์ ๊ฐ ์์ต๋๋ค", category),
problemPage // ๋ณ๊ฒฝ๋ problemPage ๋ฐํ
);
}
return BaseResponse.onSuccess(
String.format("%s ์นดํ
๊ณ ๋ฆฌ ๋ฌธ์ ์กฐํ ์ฑ๊ณต (์ด %d๊ฐ)",
category, problemPage.getTotalElements()),
problemPage // ๋ณ๊ฒฝ๋ problemPage ๋ฐํ
);
}
} ์ด ๋ฆฌ๋ทฐ๊ฐ ์ฝ๋ ํ์ง์ ๊ฐ์ ํ๋ ๋ฐ ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. |
src/main/java/site/haruhana/www/entity/Problem.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/entity/Problem.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋
ํ์ธ์. ์๋์ด ๊ฐ๋ฐ์๋ก์ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก ์ ๊ณต๋ ์ฝ๋ ์กฐ๊ฐ์ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฅ์ ์ํํ์ง๋ง, ์์ธ ์ฒ๋ฆฌ, ๋ฐ์ดํฐ ๊ฒ์ฆ, ๋ณด์, ์ฑ๋ฅ ์ธก๋ฉด์์ ๊ฐ์ ํ ์ฌ์ง๊ฐ ์์ต๋๋ค. ์์์ ์ ์๋ ์ ์๋ค์ ์ ์ฉํ์ฌ ์ฝ๋์ ํ์ง์ ๋์ด๊ณ ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ์ ์ฒด ์ฝ๋์ ๋งฅ๋ฝ๊ณผ ์๊ตฌ์ฌํญ์ ๊ณ ๋ คํ์ฌ ์ ์ ํ ๊ฐ์ ๋ฐฉ์์ ์ ํํ์๊ธฐ ๋ฐ๋๋๋ค. |
src/main/java/site/haruhana/www/repository/ProblemRepository.java ๋ฆฌ๋ทฐProblemRepository.java ์ฝ๋ ๋ฆฌ๋ทฐ๋ค์์ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ํ๋ฅญํ๋ฉฐ, ์ ์ง๋ณด์ํ๊ธฐ ์ฝ์ต๋๋ค. ์์ ์ ์๋ ์ ์์ ํ์ฌ ์ฝ๋์ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๋ ๊ฒ์ด ์๋๋ผ, ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ๋๋นํ๊ณ ์ฑ๋ฅ์ ๊ฐ์ ํ๊ธฐ ์ํ ๊ฒ์ ๋๋ค. ๋ฐ๋ผ์ ํ์ฌ ์ํฉ์ ๋ง๊ฒ ํ์ํ ๊ฐ์ ์ฌํญ์ ์ ํ์ ์ผ๋ก ์ ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. |
src/main/java/site/haruhana/www/exception/ProblemNotFoundException.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/exception/ProblemNotFoundException.java ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋ฐ์ ์ธ ํ๊ฐ: ์ ๊ณต๋ ์ฝ๋๋ ๋งค์ฐ ๊ฐ๋จํ๊ณ ๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ์ ์ ์ ์์ธ ํด๋์ค์ ๋๋ค. ์์๋๋ก ๋์ํ๋ฉฐ, ํน๋ณํ ๋ฌธ์ ๊ฐ ๋ฐ๊ฒฌ๋์ง ์์ต๋๋ค. ํ์ง๋ง ๋ช ๊ฐ์ง ๊ฐ์ ์ ๊ณผ ๊ณ ๋ คํด์ผ ํ ์ฌํญ๋ค์ด ์์ต๋๋ค. 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฒฐ๋ก :
|
src/main/java/site/haruhana/www/dto/ProblemDto.java ๋ฆฌ๋ทฐProblemDto.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋
ํ์ธ์. ์๋์ด ๊ฐ๋ฐ์๋ก์ 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ๊น๋ํ๊ณ ์ ์์ฑ๋์์ง๋ง, ๊ถ๊ธํ ์ ์ด ์์ผ์๋ฉด ์ธ์ ๋ ์ง ์ง๋ฌธํด์ฃผ์ธ์. |
src/test/java/site/haruhana/www/Service/ProblemServiceTest.java ๋ฆฌ๋ทฐProblemServiceTest.java ์ฝ๋ ๋ฆฌ๋ทฐ๋ค์์ ์ ๊ณต๋ 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก ์ ๋ฐ์ ์ผ๋ก ์ ์์ฑ๋ ํ
์คํธ ์ฝ๋์
๋๋ค. ์์์ ์ ์๋ ๊ฐ์ ์ฌํญ๋ค์ ์ ์ฉํ๋ฉด ํ
์คํธ์ ์์ฑ๋๋ฅผ ๋์ฑ ๋์ด๊ณ , ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ฅผ ์ฌ์ ์ ๋ฐ๊ฒฌํ ์ ์์ ๊ฒ์
๋๋ค. ํนํ, |
src/main/java/site/haruhana/www/converter/ProblemConverter.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/converter/ProblemConverter.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋
ํ์ธ์. ์๋์ด ๊ฐ๋ฐ์๋ก์ 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ๊น๋ํ๊ณ ์ ์์ฑ๋์์ต๋๋ค. ์์ ์ ์๋ ์ ์๋ค์ ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ์ ์๋ฐฉํ๊ณ , ์ฝ๋์ ์์ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํฅ์์ํค๋ ๋ฐ ๋์์ด ๋ ๊ฒ์
๋๋ค. ํนํ, |
src/main/java/site/haruhana/www/controller/ProblemController.java ๋ฆฌ๋ทฐProblemController.java ์ฝ๋ ๋ฆฌ๋ทฐ๋ค์์ 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์ด ๋ฆฌ๋ทฐ๊ฐ ์ฝ๋ ๊ฐ์ ์ ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. |
src/main/java/site/haruhana/www/entity/Problem.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/entity/Problem.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋์ด ๊ฐ๋ฐ์๋ก์ ํด๋น ์ฝ๋ ์กฐ๊ฐ์ ๋ฆฌ๋ทฐํ๊ฒ ์ต๋๋ค. ์ ์ฒด 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ ๋๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ์ดํดํ๊ธฐ ์ฝ๊ณ ๊น๋ํ์ง๋ง, ๋ช ๊ฐ์ง ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ๊ณผ ๊ฐ์ ํ ๋ถ๋ถ์ด ์กด์ฌํฉ๋๋ค. ์์ ์ ์๋ ์ ์๋ค์ ํตํด ์ฝ๋์ ์์ ์ฑ, ๋ณด์, ๊ทธ๋ฆฌ๊ณ ์ ์ง๋ณด์์ฑ์ ํฅ์์ํฌ ์ ์์ต๋๋ค. ํนํ, null ์ฒ๋ฆฌ, ์ ํจ์ฑ ๊ฒ์ฌ, ๋ณด์ ๋ฌธ์ ๋ ๋ฐ๋์ ํด๊ฒฐํด์ผ ํ ์ค์ํ ๋ถ๋ถ์ ๋๋ค. |
src/main/java/site/haruhana/www/Service/ProblemService.java ๋ฆฌ๋ทฐProblemService.java ์ฝ๋ ๋ฆฌ๋ทฐ๋ค์์ 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ์ ์์ฑ๋์ด ์์ง๋ง, ์์์ ์ ์๋ ๊ฐ์ ์ฌํญ๋ค์ ์ ์ฉํ๋ฉด ์ฝ๋์ ํ์ง, ์ฑ๋ฅ, ๋ณด์์ฑ์ ๋์ฑ ํฅ์์ํฌ ์ ์์ต๋๋ค. ํนํ |
src/main/java/site/haruhana/www/controller/GlobalExceptionHandler.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/controller/GlobalExceptionHandler.java ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋ฐ์ ์ธ ์ธ์: ์ฝ๋๋ ๋น๊ต์ ๊ฐ๋จํ๊ณ ๋ช
ํํ๊ฒ ์์ฑ๋์์ต๋๋ค. 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฐ์ ๋ ์ฝ๋ ์์ (์ผ๋ถ): import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.server.ResponseStatusException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import site.haruhana.www.exception.ProblemNotFoundException;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(ProblemNotFoundException.class)
public ResponseEntity<ErrorResponse> handleProblemNotFoundException(ProblemNotFoundException ex) {
ErrorResponse errorResponse = new ErrorResponse("PROBLEM_NOT_FOUND", ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException ex) {
ErrorResponse errorResponse = new ErrorResponse("INVALID_INPUT", ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(ResponseStatusException.class) // Spring ์์ ์ ๊ณตํ๋ ์์ธ
public ResponseEntity<ErrorResponse> handleResponseStatusException(ResponseStatusException ex) {
ErrorResponse errorResponse = new ErrorResponse(String.valueOf(ex.getStatusCode().value()), ex.getReason());
return new ResponseEntity<>(errorResponse, ex.getStatusCode());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
logger.error("Unexpected exception occurred: ", ex); // ๋ก๊น
์ถ๊ฐ
ErrorResponse errorResponse = new ErrorResponse("INTERNAL_SERVER_ERROR", "An unexpected error occurred."); // ์ผ๋ฐ์ ์ธ ์ค๋ฅ ๋ฉ์์ง
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
// ErrorResponse ํด๋์ค ์์ (์ค์ ๊ตฌํ์ ํ์์ ๋ฐ๋ผ ๋ณ๊ฒฝ)
private static class ErrorResponse {
private String code;
private String message;
public ErrorResponse(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
}
} ์ ์ฝ๋๋ ๋จ์ง ์์์ด๋ฉฐ, ์ค์ ๊ตฌํ์ ํ๋ก์ ํธ์ ์๊ตฌ ์ฌํญ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ์ ์์ต๋๋ค. ํต์ฌ์ ์์ธ๋ฅผ ์ธ๋ถํํ๊ณ , ์ ์ ํ HTTP ์ํ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ฉฐ, ์ค๋ฅ ์ ๋ณด๋ฅผ ๋ด์ ์๋ต ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ณ , ๋ก๊น ์ ์ถ๊ฐํ์ฌ ๋๋ฒ๊น ์ ์ฉ์ดํ๊ฒ ํ๋ ๊ฒ์ ๋๋ค. ๋ํ, ๋ฏผ๊ฐ ์ ๋ณด๊ฐ ๋ ธ์ถ๋์ง ์๋๋ก ์ฃผ์ํด์ผ ํฉ๋๋ค. |
src/main/java/site/haruhana/www/exception/ProblemNotFoundException.java ๋ฆฌ๋ทฐProblemNotFoundException.java ์ฝ๋ ๋ฆฌ๋ทฐ์ฝ๋ ๊ฐ์: ์ ๊ณต๋ ์ฝ๋๋ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ ๋๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฒฐ๋ก : ์ ๊ณต๋ ์ฝ๋๋ ๊ธฐ๋ณธ์ ์ธ ์์ธ ํด๋์ค ์ ์๋ก์ ํ์ง์ด ์ข๊ณ ๊ฐ๋ ์ฑ์ด ์ฐ์ํฉ๋๋ค. ๋ค๋ง, Javadoc์ ์ถ๊ฐํ์ฌ ์ฝ๋์ ์ค๋ช ๋ ฅ์ ๋์ด๊ณ , ํ์์ ๋ฐ๋ผ ๋ฌธ์ ID์ ๊ฐ์ ์ถ๊ฐ ์ ๋ณด๋ฅผ ํฌํจํ ์ ์๋๋ก ํ์ฅํ ์ ์์ต๋๋ค. ๋ํ, ์์ธ ๋ฐ์ ์ํฉ์ ๋ถ์ํ๊ณ ๋ ์ ์ ํ ์์ธ ์ฒ๋ฆฌ ์ ๋ต์ ์๋ฆฝํ๋ ๊ฒ์ด ์ข์ต๋๋ค. |
src/main/java/site/haruhana/www/repository/ProblemRepository.java ๋ฆฌ๋ทฐProblemRepository.java ์ฝ๋ ๋ฆฌ๋ทฐ๋ค์์ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ ๋๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก ํ์ฌ ์ฝ๋๋ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฅ์ ์ถฉ์คํ ์ํํ๊ณ ์์ผ๋ฉฐ, ์ฝ๋ ํ์ง๊ณผ ๊ฐ๋ ์ฑ๋ ์ฐ์ํฉ๋๋ค. ์์์ ์ ์๋ ๊ฐ์ ์ ์๋ค์ ํ์ฌ ์ฝ๋์ ์ฆ์ ์ ์ฉํด์ผ ํ ํ์์ ์ธ ์ฌํญ์ ์๋์ง๋ง, ํฅํ ์๊ตฌ์ฌํญ ๋ณํ์ ๋ฐ๋ผ ๊ณ ๋ คํด๋ณผ ์ ์๋ ์ฌํญ๋ค์ ๋๋ค. ํนํ ๋ค์ํ ๊ฒ์ ์กฐ๊ฑด ์ง์ ๋ฐ ์ฑ๋ฅ ์ต์ ํ๋ ์๋น์ค์ ๊ท๋ชจ๊ฐ ์ปค์ง์๋ก ๋์ฑ ์ค์ํด์ง ์ ์์ต๋๋ค. |
src/main/java/site/haruhana/www/dto/ProblemDto.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/dto/ProblemDto.java ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋ฐ์ ์ธ ํ๊ฐ: ์ ๋ฐ์ ์ผ๋ก Lombok ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ์ฌ ๋ณด์ผ๋ฌํ๋ ์ดํธ ์ฝ๋๋ฅผ ์ค์ธ ์ ์ ๊ธ์ ์ ์
๋๋ค. DTO์ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ ์ ๊ฐ์ถฐ์ ธ ์์ง๋ง, ๋ช ๊ฐ์ง ๊ฐ์ ํ ๋ถ๋ถ์ด ๋ณด์
๋๋ค. ํนํ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
์์ ๋ ์ฝ๋ ์์ (์ผ๋ถ): package site.haruhana.www.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import site.haruhana.www.entity.Problem;
import site.haruhana.www.entity.ProblemCategory;
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProblemDto {
private Long id; // ๋ฌธ์ ID
private String title; // ๋ฌธ์ ์ ๋ชฉ
private String description; // ๋ฌธ์ ์ค๋ช
private String answer; // ๋ฌธ์ ์ ๋ต
private int level; // ๋ฌธ์ ๋์ด๋
private ProblemCategory problemCategory; // ๋ฌธ์ ์นดํ
๊ณ ๋ฆฌ
public ProblemDto(Problem problem) {
this.id = problem.getId();
this.title = problem.getTitle();
this.description = problem.getDescription();
this.answer = problem.getAnswer();
this.level = problem.getLevel();
this.problemCategory = problem.getProblemCategory(); // ์์ ๋จ
}
} ์์ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐํ์ผ๋ก ์ฝ๋๋ฅผ ๊ฐ์ ํ์ฌ ๋์ฑ ๊ฒฌ๊ณ ํ๊ณ ํจ์จ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ์๊ธฐ ๋ฐ๋๋๋ค. ๊ถ๊ธํ ์ ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ง ์ง๋ฌธํด์ฃผ์ธ์. |
src/test/java/site/haruhana/www/service/ProblemServiceTest.java ๋ฆฌ๋ทฐ์ฝ๋ ๋ฆฌ๋ทฐ (ProblemServiceTest.java)๋ค์์ 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์์ (DTO ๊ฐ ๊ฒ์ฆ ์ถ๊ฐ): @Test
@DisplayName("ID๋ก ๋ฌธ์ ๋ฅผ ์กฐํํ๋ฉด ํด๋น ๋ฌธ์ ๋ฅผ ๋ฐํํ๋ค")
void getProblemById() {
// given: ๋ฌธ์ ID์ ์์๋๋ ๋ฌธ์ ๊ฐ ์ฃผ์ด์ก์ ๋
Long problemId = 1L;
Problem problem = createTestProblem(); // new Problem() ๋์ ์ด ๋ฉ์๋ ์ฌ์ฉ
ProblemDto expectedDto = ProblemDto.builder() // ์์๋๋ DTO ๊ฐ์ ์ค์
.title("ํ
์คํธ ๋ฌธ์ ")
.description("ํ
์คํธ ์ค๋ช
")
.build();
given(problemRepository.findById(problemId)).willReturn(Optional.of(problem));
given(problemConverter.toDto(problem)).willReturn(expectedDto); // ์ถ๊ฐ
// when: ID๋ก ๋ฌธ์ ๋ฅผ ์กฐํํ๋ฉด
ProblemDto result = problemService.getProblemById(problemId);
// then: ํด๋นํ๋ ๋ฌธ์ DTO๊ฐ ๋ฐํ๋๋ค
assertNotNull(result, "๋ฐํ๋ ๋ฌธ์ ๋ null์ด ์๋์ด์ผ ํฉ๋๋ค");
assertEquals(expectedDto.getTitle(), result.getTitle(), "์ ๋ชฉ์ด ์ผ์นํด์ผ ํฉ๋๋ค"); // ์ ๋ชฉ ๊ฒ์ฆ
assertEquals(expectedDto.getDescription(), result.getDescription(), "์ค๋ช
์ด ์ผ์นํด์ผ ํฉ๋๋ค"); // ์ค๋ช
๊ฒ์ฆ
verify(problemRepository).findById(problemId);
verify(problemConverter).toDto(problem); // ์ถ๊ฐ
} ๊ฒฐ๋ก : ์ฝ๋๋ ์ ๋ฐ์ ์ผ๋ก ์ ์์ฑ๋์์ผ๋ฉฐ, ๋จ์ ํ
์คํธ๋ฅผ ํตํด |
src/main/java/site/haruhana/www/controller/GlobalExceptionHandler.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/controller/GlobalExceptionHandler.java ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋ฐ์ ์ธ ํ๊ฐ: ์ฝ๋๋ ๋น๊ต์ ๊ฐ๋จํ๊ณ ๊ธฐ๋ฅ์ ์ผ๋ก๋ ๊ธ๋ก๋ฒ ์์ธ ์ฒ๋ฆฌ๋ฅผ ์ํํ๋ ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค. ๊ฐ๋ ์ฑ์ ๊ด์ฐฎ์ง๋ง, ๋ช ๊ฐ์ง ๊ฐ์ ์ ์ ํตํด ๋์ฑ ๊ฒฌ๊ณ ํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ด ์ฝ๋๋ก ๋ง๋ค ์ ์์ต๋๋ค. 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ ๋ฐ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
// src/main/java/site/haruhana/www/controller/GlobalExceptionHandler.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import site.haruhana.www.exception.ProblemNotFoundException;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(ProblemNotFoundException.class)
public ResponseEntity<ErrorResponse> handleProblemNotFoundException(ProblemNotFoundException ex) {
logger.warn("Problem not found: {}", ex.getMessage()); // ๋ฌธ์ ID ๋ก๊น
์ถ๊ฐ ๊ณ ๋ ค
ErrorResponse errorResponse = new ErrorResponse("PROBLEM_NOT_FOUND", "๋ฌธ์ ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.");
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
logger.error("Unexpected exception: {}", ex.getMessage(), ex); // ์คํ ํธ๋ ์ด์ค ๋ก๊น
ErrorResponse errorResponse = new ErrorResponse("INTERNAL_SERVER_ERROR", "์๋ฒ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// src/main/java/site/haruhana/www/dto/ErrorResponse.java
package site.haruhana.www.dto;
public class ErrorResponse {
private String errorCode;
private String errorMessage;
public ErrorResponse(String errorCode, String errorMessage) {
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public String getErrorCode() {
return errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
} ์์ ์์ ์ฝ๋๋ ๋ก๊น
, ์๋ฌ ์๋ต ํฌ๋งท ์ ์, ๋ณด์ ๊ฐํ๋ฅผ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์์ ๋ฐ์ํ ๊ฒ์
๋๋ค. ์ด๋ฌํ ๊ฐ์ ์ฌํญ๋ค์ ์ ์ฉํ๋ฉด ์ฝ๋์ ํ์ง, ๊ฐ๋ ์ฑ, ๋ณด์์ฑ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค. |
src/main/java/site/haruhana/www/converter/ProblemConverter.java ๋ฆฌ๋ทฐProblemConverter.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋
ํ์ธ์, ์๋์ด ๊ฐ๋ฐ์๋ก์ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
๊ฒฐ๋ก
๊ถ๊ธํ ์ ์ด ์์ผ์๋ฉด ์ธ์ ๋ ์ง ์ง๋ฌธํด์ฃผ์ธ์. |
src/main/java/site/haruhana/www/controller/ProblemController.java ๋ฆฌ๋ทฐProblemController.java ์ฝ๋ ๋ฆฌ๋ทฐ๋ค์์ 1. ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ:
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์ :
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ:
4. ๋ณด์ ๊ด๋ จ ์ด์:
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์:
๊ฒฐ๋ก : ์ ๋ฐ์ ์ผ๋ก ์ฝ๋๋ ์ ์์ฑ๋์์ง๋ง, ์์์ ์ธ๊ธํ ๊ฐ์ ์ฌํญ๋ค์ ์ ์ฉํ๋ฉด ์ฝ๋ ํ์ง, ์ฑ๋ฅ, ๋ณด์์ฑ์ ๋์ฑ ํฅ์์ํฌ ์ ์์ต๋๋ค. ํนํ ์์ธ ์ฒ๋ฆฌ, ์ ํจ์ฑ ๊ฒ์ฆ, ๋ณด์ ๊ด๋ จ ์ด์๋ ์ฃผ์ ๊น๊ฒ ๊ฒํ ํ๊ณ ์์ ํด์ผ ํฉ๋๋ค. |
src/main/java/site/haruhana/www/entity/Problem.java ๋ฆฌ๋ทฐsrc/main/java/site/haruhana/www/entity/Problem.java ์ฝ๋ ๋ฆฌ๋ทฐ์๋์ด ๊ฐ๋ฐ์๋ก์ ์ ์๋ ์ฝ๋ ์กฐ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ๋ค์ ์ฌํญ๋ค์ ๋ฆฌ๋ทฐํ๊ฒ ์ต๋๋ค. ์ ์ฒด ์ฝ๋๊ฐ ์๋๋ผ ์ผ๋ถ๋ง ์ฃผ์ด์ ธ ์์ด์ ์ ํ์ ์ธ ๋ถ๋ถ๋ ์๋ค๋ ์ ์ ๊ฐ์ํด์ฃผ์ธ์. 1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์์ ์ฝ๋ (์ผ๋ถ) import lombok.Builder;
import site.haruhana.www.dto.ProblemDto;
import java.util.Objects;
@Builder
public class Problem {
private String title;
private String description;
private Integer level;
private String problemCategory; // Enum ํ์
์ผ๋ก ๋ณ๊ฒฝํ๋ ๊ฒ์ ๊ณ ๋ ค
/**
* @param updatedProblemDto ๊ฐฑ์ ํ ๋ฌธ์ ์ ๋ณด๋ฅผ ํฌํจํ๋ DTO
*/
public void update(ProblemDto updatedProblemDto) {
Objects.requireNonNull(updatedProblemDto, "updatedProblemDto cannot be null");
if (updatedProblemDto.getTitle() != null && !this.title.equals(updatedProblemDto.getTitle())) {
this.title = updatedProblemDto.getTitle();
}
if (updatedProblemDto.getDescription() != null && !this.description.equals(updatedProblemDto.getDescription())) {
this.description = updatedProblemDto.getDescription();
}
if (updatedProblemDto.getLevel() != null && !this.level.equals(updatedProblemDto.getLevel())) {
this.level = updatedProblemDto.getLevel();
}
if (updatedProblemDto.getProblemCategory() != null && !this.problemCategory.equals(updatedProblemDto.getProblemCategory())) {
this.problemCategory = updatedProblemDto.getProblemCategory();
}
}
} ๊ฒฐ๋ก ์ฝ๋ ์์ฒด๋ ๊ฐ๊ฒฐํ๊ณ ์ดํดํ๊ธฐ ์ฝ์ง๋ง, ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ๊ณผ ๊ฐ์ ํด์ผ ํ ๋ถ๋ถ๋ค์ด ์กด์ฌํฉ๋๋ค. Null ์ฒดํฌ, ์ ํจ์ฑ ๊ฒ์ฌ, ๋ณด์ ๊ฐํ, ์ฑ๋ฅ ๊ฐ์ ๋ฑ์ ํตํด ์ฝ๋์ ์์ ์ฑ๊ณผ ํจ์จ์ฑ์ ๋์ผ ์ ์์ต๋๋ค. ๋ํ, ์ ์ฒด ์ฝ๋ ๋งฅ๋ฝ์ ํ์ ํ์ฌ ๋๋ฝ๋ ํ๋ ์ ๋ฐ์ดํธ๋ ๋ค๋ฅธ ์ ์ฌ์ ๋ฌธ์ ์ ๋ค์ ์ฐพ์ ํด๊ฒฐํด์ผ ํฉ๋๋ค. |
src/main/java/site/haruhana/www/service/ProblemService.java ๋ฆฌ๋ทฐProblemService.java ์ฝ๋ ๋ฆฌ๋ทฐ1. ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋ ์ฑ
2. ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ๋ฌธ์ ์
3. ์ฑ๋ฅ ๊ฐ์ ํฌ์ธํธ
4. ๋ณด์ ๊ด๋ จ ์ด์
5. ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์
์ด๋ฌํ ์ ์๋ค์ ํตํด ์ฝ๋ ํ์ง, ์ฑ๋ฅ, ๋ณด์์ ๋์ฑ ํฅ์์ํฌ ์ ์์ต๋๋ค. ๊ฐ ์ ์์ ํ๋ก์ ํธ์ ์ํฉ๊ณผ ์๊ตฌ ์ฌํญ์ ๋ง๊ฒ ์ ์ ํ ์ ์ฉํด์ผ ํฉ๋๋ค. |
src/main/java/site/haruhana/www/entity/Problem.java ๋ฆฌ๋ทฐ
|
src/main/java/site/haruhana/www/controller/ProblemController.java ๋ฆฌ๋ทฐ
|
src/main/java/site/haruhana/www/exception/ProblemNotFoundException.java ๋ฆฌ๋ทฐ
|
src/main/java/site/haruhana/www/controller/GlobalExceptionHandler.java ๋ฆฌ๋ทฐ
|
src/main/java/site/haruhana/www/repository/ProblemRepository.java ๋ฆฌ๋ทฐ
|
src/main/java/site/haruhana/www/converter/ProblemConverter.java ๋ฆฌ๋ทฐ
|
src/test/java/site/haruhana/www/service/ProblemServiceTest.java ๋ฆฌ๋ทฐ
|
src/main/java/site/haruhana/www/service/ProblemService.java ๋ฆฌ๋ทฐ
|
src/main/java/site/haruhana/www/dto/ProblemDto.java ๋ฆฌ๋ทฐ
|
feat_1: Problem ๋๋ฉ์ธ ์ํฐํฐ ๋ฐ ๊ธฐ๋ณธ ๊ตฌ์กฐ ์ถ๊ฐ
feat_2: Problem ๋๋ฉ์ธ ์๋น์ค ๊ณ์ธต ๊ตฌํ
feat_3: Problem API ์๋ํฌ์ธํธ ๋ฐ ํ ์คํธ ์ถ๊ฐ
feat_4: Problem ๋๋ฉ์ธ ์์ธ ์ฒ๋ฆฌ ๊ตฌํ