๐Ÿ“š atdd-subway-map ๋ฏธ์…˜ ์ •๋ฆฌ


์ค‘๋ณต๋˜๋Š” ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ

  • ์ค‘๋ณต๋˜๋Š” ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋Š” ์ถ”์ถœํ•˜์ž
  • ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋„ ์œ ์ง€๋ณด์ˆ˜์˜ ๋Œ€์ƒ์ด๋ฉฐ ํ•˜๋‚˜์˜ ๋ฌธ์„œ์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋…์„ฑ์„ ๊ณ ๋ คํ•˜์ž
private ExtractableResponse<Response> addSection(String content) {
  return RestAssured.given().log().all()
    .body(content)
    .contentType(MediaType.APPLICATION_JSON_VALUE)
    .when()
    .post("/lines/{id}/sections", 1L)
    .then().log().all()
    .extract();
}

์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” Service์—์„œ

  • DAO์—์„œ ์˜ˆ์™ธ๋ฅผ ์ฒดํฌํ•ด์ฃผ๊ณ  ์žˆ์—ˆ์Œ
  • DAO๋Š” ๋ง๊ทธ๋Œ€๋กœ DB์— ์–ด์„ธ์Šคํ•˜๋Š” ์—ญํ• ๋งŒ ํ•  ๋ฟ
  • ์ด ๊ฒฐ๊ณผ์— ๋Œ€ํ•œ ์˜ˆ์™ธ๋Š” ์„œ๋น„์Šค๊ฐ€ ์•Œ์•„์„œ ํ•˜๋„๋ก ํ•˜์ž

@Valid์™€ @Validated๋กœ DTO ๊ฒ€์ฆ

  • @Valid ๋ฅผ ํ†ตํ•ด DTO์—์„œ ์š”์ฒญ์— ๋Œ€ํ•œ ๊ฒ€์ฆ์„ ํ•˜๊ฒŒ ํ•ด์คŒ
  • Validation์„ groupํ™” ํ•  ์ˆ˜ ์žˆ๋Š” ์ ์„ ์ด์šฉํ•ด์„œ ๊ฐ๊ธฐ ์š”์ฒญ๋งˆ๋‹ค group์„ ์ง€์–ด ํ•œ DTO๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๊ฐ๊ฐ ์š”์ฒญ์— ๋งž๋Š” ๊ฒ€์ฆ์„ ์ง„ํ–‰

์ •๋ฆฌ

Valid๋กœ ์žก์€ ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ

  • Valid ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์žก์€ MethodArgumentNotValidException์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๋Œ€๋กœ ์ถœ๋ ฅํ•˜๊ณ  ์žˆ์ง€ ์•Š์•˜์Œ
  • ๊ฒŒ์ด์ธ ๊ฐ€ ์ œ์•ˆํ•ด์ค€ ๋ฐฉ๋ฒ•์„ ์ ์šฉํ•˜๋‹ˆ BindingResult์— ์žˆ๋Š” ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€๋“ค์„ ์ถ”์ถœํ•˜์—ฌ ๋˜์ ธ์ค„ ์ˆ˜ ์žˆ์—ˆ์Œ

Service ๋ ˆ์ด์–ด์—์„œ ๋„๋ฉ”์ธ์ด ์•„๋‹Œ DTO๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ž

  • Controller์—์„œ ๋„๋ฉ”์ธ์„ ๊ฐ€์ ธ๋‹ค ์จ์„œ ํ™”๋ฉด์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ๋„๋ฉ”์ธ ์ž์ฒด๋‚˜ ๋„๋ฉ”์ธ์˜ getter๋ฅผ ๋…ธ์ถœ์‹œํ‚ค๊ณ  ์žˆ์—ˆ์Œ
  • ๋„๋ฉ”์ธ์˜ ์ •๋ณด๋ฅผ ์™ธ๋ถ€(view)์— ๋…ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ, interface์ธ DTO๋ฅผ ํ†ตํ•ด ์„œ๋น„์Šค ๋ ˆ์ด์–ด์—์„œ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋ฆฌํŒฉํ† ๋ง
  • ์ฐธ๊ณ 

์ •๋ฆฌ

RestAssured์— ๋Œ€ํ•ด

  • ์ „ ๋‹จ๊ณ„๊นŒ์ง€๋Š” MockMVC ๋“ฑ์œผ๋กœ ์ปจํŠธ๋กค๋Ÿฌ๋‹จ์„ ๋‹จ์œ„ํ…Œ์ŠคํŠธ๋กœ ์ง„ํ–‰ํ–ˆ์Œ
  • E2E ํ…Œ์ŠคํŠธ๋ฅผ ํ•œ๋‹ค๋Š” ๊ฒƒ์€ ๋ง ๊ทธ๋Œ€๋กœ ๋๋ถ€ํ„ฐ ๋๊นŒ์ง€,์š”์ฒญ๋ถ€ํ„ฐ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ์‘๋‹ต์„ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ๊ฒƒ์ธ๋ฐ
    ์ด๋ ‡๊ฒŒ๋˜๋ฉด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋Š” ์ด์œ ๊ฐ€ ์žˆ์„๊นŒ๋ผ๋Š” ๊ณ ๋ฏผ์„ ํ–ˆ์Œ
  • ๊ณ ๋ฏผํ•œ ๊ฒฐ๊ณผ โ€œ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ๊ตฌํ˜„ ๋‹จ๊ณ„์—์„œ ๋‚ด๊ฐ€ ๊ตฌํ˜„ํ•œ ๋ ˆ์ด์–ด(๋‹จ์œ„)๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด, ์ดํ›„ ๋ชจ๋“  ๋‹จ์œ„๋“ค์ด ์กฐํ•ฉ๋˜์—ˆ์„ ๋•Œ ํ†ตํ•ฉํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…”์ด ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ž˜ ์ˆ˜ํ–‰ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•œ๋‹คโ€ ์ด๋ ‡๊ฒŒ ๊ฒฐ๋ก ์„ ๋‚ด๋ฆผ

๊ตฌ๊ฐ„ ์ถ”๊ฐ€ ๋กœ์ง

  • ๊ตฌ๊ฐ„์„ ์ถ”๊ฐ€ํ•  ๋•Œ ์ƒํ–‰์—ญ, ํ•˜ํ–‰์—ญ์ด ์กด์žฌํ•˜๋Š”์ง€ ๋˜ ์ด๋ฅผ ์ฐพ๊ณ  ์ˆ˜์ •ํ•ด์ฃผ๊ธฐ ์œ„ํ•œ ๋งŽ์€ ๋กœ์ง์„ ์ž‘์„ฑํ•ด์•ผํ–ˆ์Œ
  • ๊ทธ๋Ÿฐ๋ฐ ์‚ฌ์‹ค ๋น„์Šทํ•œ ๋กœ์ง์ธ๋ฐ ์ƒํ–‰์ธ์ง€ ํ•˜ํ–‰์ธ์ง€ ๋Œ€์ƒ๋งŒ ๋‹ฌ๋ž์Œ
  • ์•„๋ž˜์™€ ๊ฐ™์ด ์ผ๋‹จ ๊ตฌํ˜„์„ ๋ชฉ์ ์œผ๋กœ ํ–ˆ์„ ๋•Œ๋Š” ์—„์ฒญ๋‚œ ๋ถ„๊ธฐ๊ฐ€ ์ƒ๊ฒจ๋ฒ„๋ฆผ
@Transactional
public void addSection(final Long lineId, final SectionRequest sectionRequest) {
  Line line = lineRepository.findById(lineId);
  Section toAddSection = sectionRequest.toSection(lineId);
  Station targetStation = line.registeredStation(toAddSection);
  if (toAddSection.hasUpStation(targetStation)) {
    Section targetSection = line.findSectionWithUpStation(targetStation);
    checkAddableByDistance(toAddSection, targetSection);
    lineRepository.updateSection(lineId,
                                 new Section(targetSection.id(), lineId, toAddSection.downStation(), targetSection.downStation(), targetSection.subtractDistance(toAddSection)));
  }
  if (toAddSection.hasDownStation(targetStation)) {
    Section targetSection = line.findSectionWithDownStation(targetStation);
    checkAddableByDistance(toAddSection, targetSection);
    lineRepository.updateSection(lineId,
                                 new Section(targetSection.id(), lineId, targetSection.upStation(), toAddSection.upStation(), targetSection.subtractDistance(toAddSection)));
  }
  lineRepository.addSection(lineId, sectionRequest.getUpStationId(), sectionRequest.getDownStationId(), sectionRequest.getDistance());
}
  • ์ด๋Š” ์ƒํ–‰๊ตฌ๊ฐ„ ์ฐพ๊ธฐ, ํ•˜ํ–‰๊ตฌ๊ฐ„ ์ฐพ๊ธฐ๋ฅผ ์ „๋žตํŒจํ„ด์„ ์ด์šฉํ•ด ๋ฆฌํŒฉํ†  ํ•ด๋ฒ„๋ ธ์Œ
public class LineService {
    private final LineRepository lineRepository;
    private final StationDao stationDao;
    private final List<FindSectionStrategy> findSectionStrategies;
// ...
  • ์ผ๋‹จ LineService๊ฐ€ ๊ตฌ๊ฐ„์„ ์ฐพ๋Š” ์ „๋žต์„ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
@Transactional
public void addSection(final Long lineId, final SectionRequest sectionRequest) {
  Line line = lineRepository.findById(lineId);
  Section toAddSection = sectionRequest.toSection(lineId);
  Station targetStation = line.registeredStation(toAddSection);
  Section targetSection = line.findSectionWithStation(targetStation, findSectionStrategies);
  lineRepository.updateSection(lineId, targetSection.updateToAdd(toAddSection));
  lineRepository.addSection(lineId, sectionRequest.getUpStationId(), sectionRequest.getDownStationId(), sectionRequest.getDistance());
}
  • ๋Œ€์ƒ ๊ตฌ๊ฐ„์„ ์ฐพ์„ ๋•Œ ์ „๋žต๋“ค์„ ์ฃผ์ž…ํ•˜๊ณ 
public Section findSectionWithStation(Station targetStation, List<FindSectionStrategy> findSectionStrategies) {
  return findSectionStrategies.stream()
    .map(findSectionStrategy -> findSectionStrategy.findSection(sections, targetStation))
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findAny()
    .orElse(EMPTY);
}
  • Sections์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•ด๋‹น ๊ตฌ๊ฐ„์„ ์ฐพ์Œ

PR ๋งํฌ