DevLog ๐ถ
[์ฐํ ์ฝ 5๊ธฐ] ์งํ์ฒ ๋ฏธ์ ํ๊ณ ๋ณธ๋ฌธ
๊ฐ๋น์ ์งํํ ๋ ๋ฒจ 2 ์ธ ๋ฒ์งธ ํ์ด ํ๋ก๊ทธ๋๋ฐ ๋ฏธ์ ์ธ ์งํ์ฒ ๋ฏธ์ ์ด๋ค...!
์ฐํ ์ฝ ํ๋ฉด์ ์งํํ๋ ๋ฏธ์ ์ค์์ ๊ฐ์ฅ ์ด๋ ค์ด ๋ฏธ์ ์ด ์๋์๋ ์ถ๋ค.
๊ฐ์ฒด์งํฅ์ ์ธ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ๊ณผ ๋จ์ ๊ตฌํ ๊ทธ ์ฌ์ด์์ ์์ฒญ ํค๋งธ๋ ๊ฒ ๊ฐ๋ค...
์์ผ๋ก ๊ฐ๋ฐํ ๋๋ ๋ ๋ฒจ 1์์ ๋ฐฐ์ ๋ ๊ฒ๋ค์ ์ ๋ง ์์ง ์์์ผ๊ฒ ๋ค ๐ฅฒ
โ๏ธ ์์ฑํ ์ฝ๋
โ๏ธ 1์ฐจ PR
โ๏ธ 2์ฐจ PR
โ๏ธ ๊ธฐ๋ฅ ์๊ตฌ์ฌํญ
- ์งํ์ฒ ๋ ธ์ ์ ์ญ์ ๋ฑ๋ก / ์ ๊ฑฐํ๋ API ๊ตฌํ
- ํ๋์ ์ญ์ ์ฌ๋ฌ ๋ ธ์ ์ ๋ฑ๋ก๋ ์ ์์ผ๋ฉฐ, ๋ ธ์ ์ ๊ฐ๋๊ธธ์ ๊ฐ์ง ์ ์๋ค.
- ์ต์ด ๋ฑ๋ก ์ ๋ ์ญ์ ๋์์ ๋ฑ๋กํด์ผ ํ๋ค.
- ์ด๋ฏธ ๋ฑ๋ก๋ ์ญ ์ฌ์ด์ ๋ ธ์ ์ ๋ฑ๋กํ ๊ฒฝ์ฐ ๊ฑฐ๋ฆฌ ์ ๋ณด๋ฅผ ๊ณ ๋ คํด์ผ ํ๋ค.
- ๋ ธ์ ์ ๊ฑฐ ์ ๊ธฐ์กด ์ญ ๊ฐ ๊ฑฐ๋ฆฌ ์ ๋ณด๋ฅผ ์ ๋ฐ์ดํธ ํด์ค์ผ ํ๋ค.
- ๋ ธ์ ์ 2๊ฐ๋ง ์ญ์ด ๋จ์์ ๊ฒฝ์ฐ ๋ชจ๋ ์ ๊ฑฐํด์ผ ํ๋ค.
- ๋ ธ์ ์กฐํ ์ ๋ฑ๋ก๋ ์ญ๋ ํจ๊ป ์กฐํ๋๋๋ก API ๊ตฌํ
- ์ถ๋ฐ์ญ์์ ๋์ฐฉ์ญ๊น์ง ๊ฐ ์ ์๋ ์ต๋จ ๊ฒฝ๋ก ์ถ๋ ฅ ๋ฐ ์๊ธ ์ ๋ณด ์กฐํ API ์ถ๊ฐ
- ๋ ธ์ ์ ๋ํ ์ถ๊ฐ ์๊ธ ์ ์ฑ ๋ฐ ์ฐ๋ น๋ณ ์๊ธ ํ ์ธ ์ ์ฑ ์ถ๊ฐ
์ด๋ฒ์๋ ๊ณ ๋ฏผํ๋ ๋ถ๋ถ๋ค์ ๋ํด์ ์ ์ด๋ณด๊ณ ์ ํ๋ค!
๐ฌ ๋๋ฉ์ธ ์ค๊ณํ๊ธฐ
ํ์ด๋ ์งํํ๋ฉด์ DB ์์ฃผ๊ฐ ์๋, ๋๋ฉ์ธ ์์ฃผ์ ์ฝ๋๋ฅผ ์์ฑํ๊ณ ์ถ์ด์ ์ฒ์๋ถํฐ ๋๋ฉ์ธ์ ์์ฃผ๋ก ์์ฑํ์๋ค.
๋ ธ์ ๊ณผ ์งํ์ฒ ์ญ, ๊ทธ๋ฆฌ๊ณ ๋ ธ์ ์ ๋ฑ๋ก๋ ์ญ ์ ๋ณด๋ฅผ ๋ํ๋ด๊ธฐ ์ํด ๊ตฌ๊ฐ ์ ๋ณด๋ฅผ ๋ํ๋ด๋ 3๊ฐ์ ๋๋ฉ์ธ์ ๋ฉ์ธ์ผ๋ก ์ก์๋ค.
์๋ง ๋๋ถ๋ถ์ ํฌ๋ฃจ๋ค์ด ์ด๋ ๊ฒ ์ค๊ณํ ๊ฒ ๊ฐ์๋ค.
๐ฑ Station
์ฌ์ค ๋ง ๊ทธ๋๋ก ์งํ์ฒ ์ญ์ ๋ํ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์๋ ๋๋ฉ์ธ์ด๋ค.
์งํ์ฒ ์ด๋ฆ ๋ง๊ณ ํน๋ณํ ์ ๋ณด๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋ฑํ ์ค๋ช ํ ๊ฒ์ด ์๋ค ใ ใ
๐ฑ Section
๋ ธ์ ์ ๋ํ ๊ฐ ๊ตฌ๊ฐ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ๋๋ฉ์ธ์ด๋ค.
์ถ๋ฐ์ญ๊ณผ ๋์ฐฉ์ญ, ๊ทธ๋ฆฌ๊ณ ๋ ์ญ ์ฌ์ด์ ๊ฑฐ๋ฆฌ ์ ๋ณด๋ฅผ ๊ด๋ฆฌํ๊ณ ์๋ค.
์ฒ์์๋ ์ญ์ ํํ, ์ํ ์ ๋ณด๋ฅผ ์๊ธฐ ์ํด์ ์ด์ ๋ํ boolean ํ๋๋ฅผ ์ถ๊ฐํ ๊น ๊ณ ๋ฏผํ์๋๋ฐ, map์ ์ฌ์ฉํด์ ์ด๋ ํด๊ฒฐํ ์ ์๋ ๋ฌธ์ ์ฌ์ ์ถํ ์์ ํ๊ฒ ๋์๋ค. (ํ๋ธ ๊ฐ ๐)
๐ฑ Line
๋ ธ์ ๋๋ฉ์ธ์ ๊ฒฝ์ฐ ์ด๋ฆ๊ณผ ์์, ์ถ๊ฐ ์๊ธ, ๊ทธ๋ฆฌ๊ณ ๋ ธ์ ์ ๋ํ ์ญ ์ ๋ณด์ธ SubwayLine์ ๊ฐ์ง๊ณ ์๋๋ก ํ์๋ค.
Section์์ Line์ ์ฐธ์กฐํ๋ ์์ผ๋ก ์ค๊ณ๋ฅผ ํ์ ํฌ๋ฃจ๋ค๋ ๋ช๋ช ๋ดค์๋๋ฐ, ํ ์ด๋ธ์์์๋ Section ํ ์ด๋ธ์ด Line ํ ์ด๋ธ์ ์ฐธ์กฐํ๋ ๊ฒ์ด ์์ฐ์ค๋ฝ์ง๋ง ์ค์ธ๊ณ์์ '๋ ธ์ ์ด ์ญ์ ๊ฐ์ง๋ค'๋ผ๋ ์กฐ๊ฑด์ ๋ถํฉํ์ง ์๋๋ค๊ณ ์๊ฐํ๋ค.
๊ทธ๋์ ๋ ธ์ ๋๋ฉ์ธ์ด SubwayLine์ด๋ผ๋ ๋๋ฉ์ธ์ ๊ฐ์ง๋๋ก ๋ง๋ค์๊ณ , ๋๋ฉ์ธ ๊ฐ์ฒด๋ฅผ ์ค๊ณํ ๋ ์ด๋ค ์์ผ๋ก ๋ชจ๋ธ๋งํ๋ ๊ฒ ์ค์ํ์ง ์ ์ ์์๋ ์๊ฐ์ธ ๊ฒ ๊ฐ๋ค. (๋ด๊ฐ ์ง๊ธ๊น์ง ์ ๋ง DB ์ฃผ๋ ๊ฐ๋ฐ์ ํ์๊ตฌ๋ ๋ค์๊ธ ์๊ฐํ ์ ์๋ ์๊ฐ์ด์๋ค...)
๐ฑ SubwayLine
SubwayLine์ด๋ผ๋ ๋๋ฉ์ธ์ ํ๋์ ๋ ธ์ ์ ๋ํ ๊ตฌ๊ฐ ์ ๋ณด ๋ฆฌ์คํธ์ ์์๋๋ก ์ ๋ ฌ๋์ด ์๋ ์ญ ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, Line
์ฌ์ค DB์ ๊ฐ์ฅ ๊ฐ๊ทน์ด ํฐ ๋๋ฉ์ธ์ด ์๋๊น ์ถ๋ค. (์ด ๊ณผ์ ์์ ๊ฐ์ฒด์งํฅ์ ์ค๊ณ๋ฅผ ์ ๋ง ๋ง์ด ๊ณ ๋ฏผํ๋ค.)
๋๋ฉ์ธ์ด ํ ์ ์๋ ์ผ์ ์ฌ์ค ์๋น์ค์์๋ ์ถฉ๋ถํ DB ์กฐํ๋ฅผ ํตํด์ ํด๊ฒฐํ ์ ์๋ ์ผ๋ค์ด๊ธฐ ๋๋ฌธ์ด๋ค.
๊ฐ์ฒด์๊ฒ ์ฑ ์์ ์ฃผ๊ธฐ ์ํด DB์์ ์กฐํํด์จ ๋๋ฉ์ธ๊ณผ ์ค์ ์ฐ์ฐ์ ํตํด ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ๋ํด์ ๋ณ๊ฒฝ ๊ฐ์ง (ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ด์ฉํ๊ฑฐ๋, ํน์ ์ฐจ์งํฉ์ ์ด์ฉํ๊ฑฐ๋...)๋ฅผ ํ์ฉํ์ฌ ์ ๋ฐ์ดํธ๋ฅผ ํ ํฌ๋ฃจ๋ค๋ ๋ณด์์๋๋ฐ, ์คํ๋ ค ํ๋ก๊ทธ๋จ์ ๋ณต์ก๋๊ฐ ๋๋ฌด ์ฆ๊ฐํ๋ ๊ฒ ๊ฐ์์ ์ค์ sections ์ ๋ณด๊ฐ ์ ๋ฐ์ดํธ ๋๋๋ผ๋ ๋๋ฉ์ธ์ ํ๋๋ ๋ณ๊ฒฝ๋์ง ์๋๋ก ํ๋ค.
๋์ , ์๋ก์ด ๊ตฌ๊ฐ ์ ๋ณด๊ฐ ๋ค์ด์ฌ ์ ์๋์ง์ ๋ํ '๊ฒ์ฆ ์์ '์ ์์ฃผ๋ก ๋๋ฉ์ธ์๊ฒ ์ฑ ์์ ์ ๊ฐํ๋๋ก ์ค๊ณํ์๋ค.
์ฝ๋ ๋ถ๋ถ์ ๋์ถฉ ๊ฐ์ ธ์๋ค. ์ด๋ฐ ์์ผ๋ก Line ๋๋ฉ์ธ์ ์์ฑํ ๋ค์์, subwayLine์ ๋ฝ์๋๋ค.
๐ก ์ฌ๊ธฐ์ Line์ subwayLine์ ๋ค๋ฆ๊ฒ ์ ๋ฐ์ดํธ ํ๋ ์ด์ ๋ ๋จ์ํ ๋ผ์ธ์ ๋ํ ์กฐํ ํ, ๋ผ์ธ๊ณผ ์ฐ๊ด๋ ์น์ ์ ๋ณด๋ฅผ ๋ฐ๋ก ์กฐํํ๊ธฐ ๋๋ฌธ์ด๋ค.
findById๋ฅผ ํตํด์ ์์ฒญ๋ฐ์ ๋ ธ์ ์ ์์ด๋๊ฐ ์ ํจํ์ง ๊ฒ์ฆํ๊ณ , ์ ํจํ๋ค๋ฉด ์น์ ์ ๋ณด๋ฅผ ์กฐํํ๋ ์์ผ๋ก ๊ตฌํํ๋ ค๊ณ ํ๋ค ๋ณด๋ ์ ๋ฐ ๊ตฌ์กฐ๊ฐ ๋๋ค. ๋ํ, findById์์ ๊ตณ์ด ์ฌ๋ฌ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ค๊ณ ์กฐํฉํ๋ ๊ฒ๋ณด๋ค ๋จ์ํ line์ ๋ํ ์ ๋ณด๋ง ์กฐํ๋๋๋ก ๋ง๋ค๊ณ ์ถ์๊ธฐ ๋๋ฌธ์ด๋ค. findById๋ ๋น๊ต์ ๊ฐ๋ณ๊ฒ ์ฐ์ด๋ ๋ฉ์๋๋ผ๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ด๋ค... ใ ใ
๊ทธ๋ฆฌ๊ณ validateSection์ ํตํด์ ์์ฒญ๋ฐ์ ๋๋ฉ์ธ์ ๋ํ ๊ฒ์ฆ ์์ ์ ์งํํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ด๋ถ์ ์ผ๋ก ์์ฒญ๋ฐ์ ์ ๋ณด๊ฐ ๋น์ด์๋์ง, ํน์ ์ด๋ฏธ ๋ฑ๋ก๋ ์ ๋ณด์ธ์ง, ์ค์ ๋ก ์ ์ฅ๋ ์ญ ์ ๋ณด์ธ์ง ํ์ธํ๊ฒ ๋๋ค.
์ด๋ฐ ์์ผ๋ก ๋๋ฉ์ธ์ด ๋ด๋ถ์ ์ผ๋ก ๊ฒ์ฆํ ์ ์๋ ๋ด์ฉ์ ๊ฒ์ฆํ๋๋ก ๋ง๋ค๋ฉด์ ์ฑ ์์ ๋๊ธฐ๊ณ , ์ค์ DB์ ์ฝ์ , ์ญ์ , ์์ ํด์ผ ํ๋ ๋ถ๋ถ์ ์๋น์ค์๊ฒ ์ฑ ์์ ๋๊ฒจ ๊ตฌํํ์๋ค.
๋ํ, SubwayLine์ด ์ ๋ ฌ๋ ์ญ ์ ๋ณด์ ๋ํด์ ํ๋๋ก ๊ด๋ฆฌํ๊ณ ์๋ ๊ฒ ์ฝ๊ฐ ์ฐ์ฐํ๊ธฐ๋ ํ์๋ค.
sections ์ ๋ณด๋ฅผ ํตํด์ ์ญ ์ ๋ณด๋ ์ถฉ๋ถํ ์ ์ถ ๊ฐ๋ฅํ ์ ๋ณด์ด๊ธฐ ๋๋ฌธ์, ์ฌ์ค ํ๋๋ก ๊ด๋ฆฌํ์ง ์๊ณ ํ์ํ ๋๋ง๋ค ์ ๋ ฌํด์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์๊ธฐ ๋๋ฌธ์ด๋ค.
ํ์ง๋ง... ๊ตณ์ด ์ฒ์ ํ ๋ฒ ์ ๋ ฌํ๋ฉด ๋๋ ์ ๋ณด๋ฅผ ์กฐํํ ๋๋ง๋ค ์ ๋ ฌํ๊ณ ์ถ์ง ์์์ ๊ทธ๋ฅ ์์ ๊ฐ์ด ํ๋๋ก ๋๋ ๋ฐฉํฅ์ ํํ๋ค.
๋ฆฌ๋ทฐ์ด๋๋ ๋ค๋ฅธ ๋ง์ ์ ํ์ จ๊ธฐ ๋๋ฌธ์ ๋๋ฆ ํฉ๋ฆฌ์ ์ธ ๊ตฌ์กฐ๊ฐ ์๋์๋ ์ถ๋ค.
private List<Station> sort(final List<Section> sections) {
final Map<Station, Station> stationRelationShip = getStationRelationShip(sections);
final Optional<Station> startStation = getStartStation(stationRelationShip);
return startStation.map(station -> getSortedStations(stationRelationShip, station))
.orElse(Collections.emptyList());
}
private Map<Station, Station> getStationRelationShip(final List<Section> sections) {
return sections.stream()
.collect(Collectors.toMap(Section::source, Section::target));
}
private Optional<Station> getStartStation(final Map<Station, Station> stationRelationShip) {
return stationRelationShip.keySet().stream()
.filter(station -> !stationRelationShip.containsValue(station))
.findFirst();
}
private List<Station> getSortedStations(final Map<Station, Station> stationRelationShip,
final Station startStation) {
final List<Station> sortedStations = new ArrayList<>();
Optional<Station> nextStation = Optional.ofNullable(startStation);
while (nextStation.isPresent()) {
sortedStations.add(nextStation.get());
nextStation = Optional.ofNullable(stationRelationShip.get(nextStation.get()));
}
return sortedStations;
}
๊ฐ์ธ์ ์ผ๋ก ์ ๋ ฌ ๋ก์ง์ ๋ํด์ ์คํธ๋ฆผ์ ์ ์ ํ ํ์ฉํ ๊ฒ ๊ฐ์ ๋ฟ๋ฏํ๋ค ๐
๐ฌ ํ ์ด๋ธ ์ค๊ณํ๊ธฐ
ํ ์ด๋ธ ๊ตฌ์กฐ๋ ์๋์ ๊ฐ์ด ์ค๊ณํ์๋ค.
CREATE TABLE IF NOT EXISTS station
(
id bigint auto_increment NOT NULL,
name VARCHAR(255) NOT NULL UNIQUE,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS line
(
id bigint auto_increment NOT NULL,
name VARCHAR(255) NOT NULL UNIQUE,
color VARCHAR(20) NOT NULL,
extra_fare int NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS section
(
id bigint auto_increment NOT NULL,
line_id bigint NOT NULL,
source_station_id bigint NOT NULL,
target_station_id bigint NOT NULL,
distance int NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(line_id) REFERENCES line(id) ON DELETE CASCADE,
FOREIGN KEY(source_station_id) REFERENCES station(id),
FOREIGN KEY(target_station_id) REFERENCES station(id)
);
์ฌ์ค ์ธ๋ํค์ ๋ํด์๋ ์ ๋ง ์๊ฒฌ์ด ๋ถ๋ถํ๊ฒ ๋๋๋๋ฐ, ๊ฐ์ธ์ ์ผ๋ก ์ด๋ ๊ฒ ์์ ๊ท๋ชจ๋ผ๋ฉด ์ธ๋ํค๋ฅผ ๊ฑธ์ด๋๋ ๊ฒ ๋ ์ข์ ๊ฒ ๊ฐ๋ค.
๋ฌผ๋ก ๊ทธ๋งํผ ํ ์คํธ๊ฐ ๋นก์ธ์ง๋ง... ํ ์คํธ๋ ํฝ์ค์ฒ๋ฅผ ํ์ฉํ์ฌ ์ด๋ ์ ๋ ํด์ํ ์ ์์ ๊ฒ ๊ฐ๋ค.
๊ทธ๋ฆฌ๊ณ , section ํ ์ด๋ธ์ ํ๋์๋ on delete cascade ์ต์ ์ ์ฃผ์๋๋ฐ, ์ด๋ ๋ถ๋ชจ ํ ์ด๋ธ์ row๊ฐ ์ ๊ฑฐ๋์์ ๋ ํจ๊ป ์ ๊ฑฐ๋์ด section์ ๊ณ ์ ํ๋(?)๊ฐ ๋จ์ง ์๋๋ก ๋ง๋ค์๋ค.
์์์๋ ๋งํ๋ ๊ฒ์ฒ๋ผ section์ ๊ฒฝ์ฐ line์ ์ ๋ณด์ ์ฐ๊ด๋์ด ์๋ ์ ๋ณด์ด๊ธฐ ๋๋ฌธ์, ๋ ธ์ ์ด ์ ๊ฑฐ๋ ์ดํ์๋ ํด๋น ๋ ธ์ ์ ๊ตฌ๊ฐ ์ ๋ณด๊ฐ ์กด์ฌํ ํ์๊ฐ ์๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ฆฌ๊ณ , ์๋ ๊ตฌํํ ๋๋ station ์ชฝ์๋ on delete cascade๋ฅผ ๊ฑธ์ด๋์์๋๋ฐ, ์๊ฐํด๋ณด๋๊น ์ญ ์ ๋ณด๊ฐ ์ ๊ฑฐ๋๋ค๊ณ ํด์ ๊ตฌ๊ฐ ์ ๋ณด๋ฅผ ๋ฐ๋ก ์ ๊ฑฐํด๋ฒ๋ฆฌ๋ฉด ๊ธฐ์กด ๊ตฌ๊ฐ์ ๋ํ ๊ฑฐ๋ฆฌ ์ ๋ฐ์ดํธ๊ฐ ์ ๋ ๊ฒ ๊ฐ์์ ํ๊ณ ๋ฅผ ์์ฑํ๋ฉฐ ๊ฐ๋จํ๊ฒ ๋ฆฌํฉํฐ๋ง์ ํด๋ณด์๋ค.
public int deleteById(final Long id) {
try {
final String sql = "DELETE FROM station WHERE id = ?";
return jdbcTemplate.update(sql, id);
} catch (final DataIntegrityViolationException e) {
throw new BadRequestException(ErrorCode.STATION_DELETE_IF_EXISTS_SECTION);
}
}
๊ทธ๋ฅ ์ด๋ ๊ฒ FK์ ๋ํ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์ด๋ฏธ ๊ตฌ๊ฐ์ ๋ํ ์ ๋ณด๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ญ์ ์ ๊ฑฐํ ์ ์๋ค๋ ์์ธ๋ฅผ ๋ฐํํ๋๋ก ๋ง๋ค์๋ค.
์คํํ๋ฉด ์๋ฐ ์์ผ๋ก ์ ๊ฑฐํ ์ ์๋๋ก ๋ง๋ค์๋ค! ๐
๐ฌ ์ธํฐํ์ด์ค๋ก ์์กด์ฑ ์ญ์ ์ํค๊ธฐ (DIP)
์ด๋ฒ ๋ฏธ์ ์์ ๊ฐ์ฅ ์ ๊ฒฝ์ด ๋ถ๋ถ ์ค์ ํ๋์ด๋ค.
์ ๋ฒ ์ฅ๋ฐ๊ตฌ๋ ๋ฏธ์ ํ๊ณ ๊ธ์ ์์ฑํ๋ฉด์ ๋ง์ง๋ง์ ๋ค์๊ณผ ๊ฐ์ ์ปค๋ฉํธ๋ฅผ ๋จ๊ฒผ์๋ค.
์ง๋ ๋ฏธ์ ์ ์์กด์ฑ์ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ์๋ค.
์๋น์ค์์ ๋ฐ๋ก Dao๋ฅผ ์ฐธ์กฐํ๋ค ๋ณด๋, Dao๊ฐ ๋ฐํํ๋ ๊ฐ์ธ Entity์ ์์กด ๊ด๊ณ๊ฐ ์๊ธฐ๊ณ , ์ด ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ๋๋ฉ์ธ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค ๋ณด๋ ์๋น์ค๊ฐ ๋๋ฉ์ธ๊น์ง ์๊ฒ ๋๋ ๊ตฌ์กฐ๊ฐ ๋ ๊ฒ์ด๋ค.
๊ทธ๋์ ์ด๋ฒ ๋ฏธ์ ์์๋ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํ์๋ค.
Repository ๊ณ์ธต์ ์ถ๊ฐํด์ ์๋น์ค ๋ ์ด์ด๋ ๋๋ฉ์ธ๋ง ๋ฐ๋ผ๋ณด๊ณ , ๋๋ฉ์ธ๊ณผ ์ํฐํฐ๋ฅผ ๋ณํํ๋ ์์ ์ Repository์๊ฒ ์์ํ ๊ฒ์ด๋ค.
์ด ๋๋ถ์ ๊ธฐ์กด์ ์ํ ๊ด๊ณ๋ฅผ ์์จ ์ ์์๋ค.
ํ์ง๋ง, DAO์ Repository๋ Persistence layer์ ์กด์ฌํ๊ณ ์๋ค.
application layer๊ฐ Repository๋ฅผ import ํ๋ฉด์ ์ฌ์ ํ persistence layer๋ฅผ ์์กดํ๋ฉฐ domain ํจํค์ง์ ์กด์ฌํ๋ ๋๋ฉ์ธ ๊ฐ์ฒด์ ๋ชจ๋ ์ํธ ์ฐธ์กฐํ๊ฒ๋๋ค๋ ๋จ์ ์ด ์กด์ฌํ๋ค.
โญ๏ธ ์ด๋ฅผ ์ํด Repository์ ์ธํฐํ์ด์ค๋ ๋๋ฉ์ธ ๋ ์ด์ด์ ๋๊ณ , ์ด์ ๋ํ ๊ตฌํ์ฒด๋ฅผ persistence layer์ ๋์ด ์์กด ๊ด๊ณ๋ฅผ ์ญ์ ์ํค๋ ์ ๋ต์ ์ฑํํ์๋ค! ๋๋ถ์ Application Layer์์๋ ๋๋ฉ์ธ ๊ณ์ธต์ ํจํค์ง๋ง ์์กดํ๊ฒ ๋์ด์ ๋ ์ด์ ์ํ ์ฐธ์กฐ๊ฐ ๋ฐ์ํ์ง ์์๋ค.
์ค์ ๋ก ์๋น์ค ๋ ์ด์ด์ import๋ฅผ ๋ณด๋ฉด application, domain์ผ๋ก๋ง ์ฐธ์กฐ ๊ด๊ณ๊ฐ ํ์ฑ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ค์ ๋ก ๊ทธ๋ฆผ์ผ๋ก ์ค๋ช ํ์๋ฉด ์ด๋ฐ ๋๋์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
๐ฌ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ ์์กด์ ์ธ ํํ๋ก ์ค๊ณํ๊ธฐ
์ต๋จ ๊ฒฝ๋ก๋ฅผ ๊ตฌํ๊ธฐ ์ํ์ฌ jgrapht ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ด์ผ ํ๋๋ฐ, ๋ถ๋๋ฝ์ง๋ง ๋ด ์ฒ์ ์ ์ถ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์๋ค.
// RouteService.java
private WeightedMultigraph<Station, DefaultWeightedEdge> getPossibleRouteGraph(
final Map<Long, List<LineWithSectionRes>> sectionsByLineId) {
final WeightedMultigraph<Station, DefaultWeightedEdge> routeGraph = new WeightedMultigraph<>(
DefaultWeightedEdge.class);
sectionsByLineId.values()
.forEach(sectionRes -> addRouteBySectionRes(routeGraph, sectionRes));
return routeGraph;
}
์ ์ฒด ์ฝ๋๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌด ๊ธธ์ด ์ผ๋ถ๋ง ๊ฐ์ ธ์์ง๋ง, ์ ๋ฐ ์์ผ๋ก ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ ๊ทธ๋๋ก ์ฌ์ฉํ๊ณ ์์๋ค.
๋น์ฐํ๊ฒ ๋ฆฌ๋ทฐ์ด๋์๊ฒ๋ ๊ฐ์ ์ง์ ์ ๋ฐ์์ ๋ถ๋๋ฌ์ ๋ค ๐ฅฒ
์ด๋ค ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ์ข์์ง ๊ณ ๋ฏผํ๋ค, ์ธํฐํ์ด์ค๋ฅผ ํ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ์๋ค.
public interface GraphProvider {
List<Station> getShortestPath(final List<SubwayLine> sections, final Station sourceStation,
final Station targetStation);
}
๋๋ฉ์ธ ๊ณ์ธต์ ๋ค์๊ณผ ๊ฐ์ ์ธํฐํ์ด์ค๋ฅผ ๋ง๋ค๊ณ (์ด๋ฆ์ ์์ฒญ ๊ณ ๋ฏผํ์๋๋ฐ ๋๋ฆ... ๊ด์ฐฎ๋?), ์ธ์๋ก ์ถ๋ฐ์ญ์์ ๋์ฐฉ์ญ๊น์ง ๊ฐ ์ ์๋ ๋ชจ๋ ๊ตฌ๊ฐ ์ ๋ณด์ ์ถ๋ฐ์ญ, ๋์ฐฉ์ญ ์ ๋ณด๋ฅผ ๋๊ฒจ์ฃผ์๋ค.
๊ทธ๋ฆฌ๊ณ , ์ธ๋ถ ์๋น์ค์์ ๋ํ๋ด๋ 'infra' ํจํค์ง์ JgraphService๋ฅผ ์ถ๊ฐํ์๋ค.
@Component
public class JgraphtService implements GraphProvider {
@Override
public List<Station> getShortestPath(final List<SubwayLine> sections,
final Station sourceStation,
final Station targetStation) {
final WeightedMultigraph<Station, DefaultWeightedEdge> routeGraph = createRouteGraph(sections);
validateRequestRoute(sourceStation, targetStation, routeGraph);
return getShortestPath(sourceStation, targetStation, routeGraph);
}
...
}
์ฌ์ค ๋ด๋ถ๋ ๊ทธ๋ฅ ์ต๋จ ๊ฒฝ๋ก ๊ตฌํ๋ ์ฝ๋์ฌ์ ์๋ฏธ๋ ์์ ๊ฒ ๊ฐ์ ์ฒจ๋ถํ์ง ์์๋ค.
์ธ์๋ก ๋๊ฒจ์ฃผ๋ ๊ฐ์ด ๋ง๊ธฐ๋ ํ์ง๋ง, ๋ด๋ถ์ ์ผ๋ก ์ํ๋ฅผ ๊ฐ์ง์ง ์๊ฒ ํ๋ ค๋ฉด ์ต๋จ ๊ฒฝ๋ก๋ฅผ ๊ตฌํ๋ ๋ฉ์๋์์ ๊ฒฝ๋ก๋ ๊ตฌํ๊ณ , ์ ๋ ฅ๊ฐ์ด ์ฌ๋ฐ๋ฅธ ๊ฐ์ธ์ง์ ๋ํ ๊ฒ์ฆ๊น์ง ๋ค ํด์ผ ๋๋ค.
์ด๋ ๊ฒ ์ค๊ณํ ๋๋ถ์ application layer์์๋ ๋๋ฉ์ธ ๊ณ์ธต์ GraphProvider ์ธํฐํ์ด์ค๋ง ๋ฐ๋ผ๋ณด๊ณ ์์ด ์ถํ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๊ต์ฒด๋๋๋ผ๋ application layer์ ๋ํ ๋ณ๊ฒฝ์ฌํญ์ ์์ด ์ํฅ ๋ฒ์๋ฅผ ์ต์ํํ ์ ์๊ฒ ๋์๋ค.
๐ฅน ๊ฐ์ธ์ ์ผ๋ก infra ํจํค์ง๋ ์ ํญ์ ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ผ๊น ๊ถ๊ธํ๋๋ฐ... ์ด๋ ๊ฒ ์ง์ ๊ตฌ์ฑํ๋ฉด์ ์์ ํ ์ดํดํ ์ ์์๋ค.
๐ฌ ์๊ธ ์ ์ฑ ํจ์จ์ ์ผ๋ก ์ ์ฉํ๊ธฐ
๋ถ๋๋ฝ๊ฒ๋ ์๊ธ ์ ์ฑ ์ญ์ ์ฒ์์ ์ง ์ฝ๋๊ฐ ๊ต์ฅํ ๋ณ๋ก๋ค.
public Fare calculateFare(final TotalDistance distance) {
if (distance.lessThan(EXTRA_FARE_SECTION_TEN)) {
return BASIC_FARE;
}
if (distance.lessAndEqualsThan(EXTRA_FARE_SECTION_FIFTY)) {
final TotalDistance extraDistance = distance.subtract(EXTRA_FARE_SECTION_TEN);
return BASIC_FARE.add(calculateByDistance(extraDistance, FARE_SECTION_UNIT_FIVE));
}
final TotalDistance fareSectionDistance = EXTRA_FARE_SECTION_FIFTY.subtract(EXTRA_FARE_SECTION_TEN);
final TotalDistance extraDistance = distance.subtract(EXTRA_FARE_SECTION_FIFTY);
final Fare extraFare = calculateByDistance(fareSectionDistance, FARE_SECTION_UNIT_FIVE)
.add(calculateByDistance(extraDistance, FARE_SECTION_UNIT_EIGHT));
return BASIC_FARE.add(extraFare);
}
10km, 10~50km, 50km ์ด์์ ๋ํด ๊ฐ๊ฐ ๋ถ๊ธฐ๋ฌธ์ ํตํด ์ฒ๋ฆฌํ๊ณ ์๋ค ๋ณด๋ ํ์ฅ์ ๋งค์ฐ ์ทจ์ฝํ ๊ตฌ์กฐ๊ฐ ๋ ๊ฒ์ด๋ค.
OCP๋ฅผ ์๋ฐํ๊ณ ์์ด์. ์ด๋ป๊ฒ ๊ฐ์ ํด๋ณผ์ ์์๊น์? (Hint. ํด๋น ๊ตฌ๊ฐ์ ํ๋จํ๋์ง ์ฌ๋ถ์ ์๊ธ์ ๊ณ์ฐํ๋ ๋ฉ์๋๊ฐ ๊ณตํต์ ์ด๋ค์)
์๊ธฐ์ ๋ฆฌ๋ทฐ์ด๋์ด ๊ต์ฅํ ํํธ๋ฅผ ์ฃผ์ จ๋ค. ๊ตฌ๊ฐ์ ํ๋จํ๋ ๋ฉ์๋์ ์๊ธ์ ๊ณ์ฐํ๋ ๋ฉ์๋๋ฅผ ๋๋๋ผ๋ ๊ฒ์ด๋ค.
๊ทธ๋์ ์์ ์ ์ฑ ๋ณ๋ก ํด๋์ค๋ฅผ ๋๋๊ธฐ๋ก ๊ฒฐ์ ํ์๋ค.
์ฐ์ , ๊ฐ ์ ์ฑ ์ ๋ํด ๊ณตํต์ ์ธ ํ์๋ฅผ ์ ์ํ๋ ์ธํฐํ์ด์ค๋ฅผ ์์ฑํด๋์๋ค.
public interface FarePolicy {
boolean isAvailable(final Distance distance);
Fare calculateFare(final Distance distance);
}
isAvailable๊ฐ ๊ตฌ๊ฐ์ ๋ํด ์ ์ฑ ์ ์ฉ์ด ๊ฐ๋ฅํ์ง ํ๋จํ๋ ๋ฉ์๋์ด๊ณ , calculateFare๊ฐ ์ค์ ๋ก ์๊ธ์ ๊ณ์ฐํ๋ ๋ฉ์๋์ด๋ค.
๊ทธ๋ฆฌ๊ณ , ํด๋น ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ 3๊ฐ์ ๊ตฌ์ฒด ์ ์ฑ ํด๋์ค๋ฅผ ์์ฑํ์๋ค.
10km ์ดํ์ ๋ํ ๊ธฐ๋ณธ ์ ์ฑ ์ BasicFarePolicy, 10~50km๋ UnitFiveFarePolicy, 50km๋ UnitEightFarePolicy๋ก ์ค์ ํ๋ค.
์ด๋ฆ์ด ์ข ์์ฝ๊ธด ํ๋ฐ... 5km๋น, 8km๋น 100์์ฉ ์ถ๊ฐ๋๋ค๋ ๊ฒ ๋ ์ค์ํ ๊ฒ ๊ฐ์์ ์ ๋ ๊ฒ ๋ง๋ค์ด๋์๋ค ใ ใ
๊ณตํต์ ์ผ๋ก ์ฌ์ฉํ๋ ํ๋ ๊ฐ์ enum์ ํตํด์ ์ ์ํด๋์๋ค. (ํ ๋ฒ์ ๊ด๋ฆฌํ๊ธฐ ์ํด์)
๊ทธ๋ฆฌ๊ณ , ์ค์ํ ์ ์ ๊ฐ ์ ์ฑ ๋ค์ 'FarePolicyComposite'๋ผ๋ ํด๋์ค๋ฅผ ํตํด ํ ๋ฒ์ ๊ด๋ฆฌํ๊ณ ์๋ ๊ฒ์ด๋ค.
public Fare getTotalFare(final Distance shortestDistance) {
return farePolicies.stream()
.filter(policy -> policy.isAvailable(shortestDistance))
.map(policy -> policy.calculateFare(shortestDistance))
.findFirst()
.orElseThrow();
}
ํด๋น ์ปดํฌ์งํธ ํด๋์ค๋ฅผ ํตํด, ๊ฐ ์ ์ฑ ๋ค์ ํ๋์ฉ ์ํํ๋ฉด์ ์ ์ฉ ๊ฐ๋ฅํ ์ ์ฑ ์ธ์ง ํ๋จํ ๋ค, ์ ์ฉ ๊ฐ๋ฅํ๋ค๋ฉด ์งํํ๋ค.
ํ์ฌ ์ค๊ณ์ ๊ตฌ๊ฐ ์ ๋ณด๋ ๊ฐ ์ ์ฑ ์ค ํ๋์ ๋ฒ์์ ๋ฌด์กฐ๊ฑด ๋ค์ด๊ฐ๊ธฐ ๋๋ฌธ์ orElseThrow()๋ก ๋์ ธ์ง ์ํ์ด ์์ ๊ฒ์ด๋ผ ํ๋จํ์๊ณ , ์ด๋ ๊ฒ ๋๋ฉด 60km ์ด์์ ๊ตฌ๊ฐ์์๋ ๋ค๋ฅธ ์ ์ฑ ์ ์ถ๊ฐํด์ผ ํ ๋ farePolices ๋ฆฌ์คํธ์ ์ ์ฑ ์ ์ถ๊ฐํด์ฃผ๊ธฐ๋ง ํ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ํ์ฅ์ ์ด๋ฆฐ ๊ตฌ์กฐ๋ฅผ ์ ์งํ ์ ์์๋ค.
๐ก ๋จ, ์ด ๊ตฌ์กฐ๋ฅผ ์ฑํํ๊ธฐ ์ํด์๋ isAvailable์ ์กฐ๊ฑด ์ ๋ณด๊ฐ ์ด์ดํด์ผ ๋๋ค๋ ์ ์ด๋ค. ํ์ฌ ๊ตฌ์กฐ๋ ์ด์ดํ๊ฒ ์ค๊ณ๋์ด ๊ด์ฐฎ์ง๋ง!
๊ทธ๋ ๋ค๋ฉด, ์ ์ฑ ๋ฆฌ์คํธ๋ ์ด๋์์ ์ถ๊ฐํด์ค๊น?
๊ฐ ์ ์ฑ ๋ค์ ์คํ๋ง ๋น์ผ๋ก ๊ด๋ฆฌ๋๊ณ ์๊ธฐ ๋๋ฌธ์ @Configuration์ ํ์ฉํ๋ฉด ์ฝ๊ฒ ์ฃผ์ ์ด ๊ฐ๋ฅํด์ง๋ค.
@Configuration
public class FarePolicyConfig {
@Bean
public FarePolicyComposite farePolicies(
final FarePolicy basicFarePolicy,
final FarePolicy unitFiveFarePolicy,
final FarePolicy unitEightFarePolicy
) {
return new FarePolicyComposite(
List.of(basicFarePolicy, unitFiveFarePolicy, unitEightFarePolicy)
);
}
}
์ด๋ ํ๋ผ๋ฏธํฐ๋ก ์ฃผ์ ๋๋ ํด๋์ค์ ์ด๋ฆ์ ๊ผญ ๋น์ ๋ฑ๋ก๋ ํด๋์ค์ ์ด๋ฆ๊ณผ ๋ง์ถฐ์ค์ผ ํ๋ค.
๐ก ์คํ๋ง์์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋ผ๋ฏธํฐ์ ํ์ ์ ๊ธฐ์ค์ผ๋ก ๋น์ ์กฐํํ๋ค.
๋ง์ฝ, ๊ฐ์ ํ์ ์ ๋น์ด ์ฌ๋ฌ ๊ฐ ์กฐํ๋๋ฉด ์ถ๊ฐ์ ์ผ๋ก ํ๋๋ ํ๋ผ๋ฏธํฐ ์ด๋ฆ์ผ๋ก ๋น์ ์กฐํํ๋ค.
์์ ์๋ฆฌ๋ฅผ ๋ฐํ์ผ๋ก 'FarePolicyComposite' ํ์ ์ 'farePolicies'๋ผ๋ ์ด๋ฆ์ ๊ฐ์ง ๋น์ ๋ฑ๋กํ๊ธฐ ๋๋ฌธ์, application layer์์๋ ๋ค์๊ณผ ๊ฐ์ด ์ฃผ์ ๋ฐ์์ ์ฌ์ฉํ๋ฉด ๋๋ค.
@Service
public class RouteService {
...
private final FarePolicyComposite farePolicies;
}
์ด๋ฅผ ๋ฐํ์ผ๋ก ์ถ๊ฐ ์๊ธ ์ ์ฑ ๋ ๋์ผํ๊ฒ ๊ตฌํํ ์ ์์๋ค ใ ใ
๋ ๊ฐ์ ๊ฒฝ์ฐ๋ ๋์ด ์ ๋ณด๋ฅผ ๋ฐ๋ก ๋ฐ์ง ์๊ณ , ๊ทธ๋ฅ ํ ์ธ๋ ๊ฐ๊ฒฉ๋ ํจ๊ป ๋ฐํํ๋๋ก ๋ง๋ค๋ค ๋ณด๋ ๊ฐ๋ฅํ๋ ๊ฒ ๊ฐ๋ค.
public DiscountFare getDiscountFares(final Fare totalFare) {
final List<Fare> fares = discountFarePolicies.stream()
.map(policy -> policy.calculateFare(totalFare))
.collect(Collectors.toUnmodifiableList());
return new DiscountFare(fares.get(0), fares.get(1));
}
๋จ, ์ด๋๋ ๋น์ผ๋ก ๋ฑ๋ก๋ ์์์ ๋ฐ๋ผ์ ์ํฅ์ ๋ฐ๋ค๋ณด๋๊น ์กฐ๊ธ ์ ๋งคํ๋ค.
๋ง์ฝ ๋์ด๋ฅผ ์ ๋ ฅ๋ฐ๋๋ค๋ฉด ์ด๋ ๊ฒ map -> collect ๋์ ์ ์๊ธ ์ ์ฑ ์ ์ ์ฉํ๋ ๊ฒ์ฒ๋ผ filter๋ฅผ ํตํด ๋ฐ์ ์ ์์ ๊ฒ ๊ฐ๋ค.
์ธ ๋ด์ฉ์ด ๋ณ๋ก ์์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋๋ฐ ์ฐ๋ค ๋ณด๋๊น ์ค๋ ํ๋ฃจ๊ฐ ๋ค ๊ฐ๋ฒ๋ ธ๋ค ๐
์ด๋ฒ ๋ฏธ์ ์ ์ ์ ์๋ ๊ฒ๋งํผ ๋๋ฆ ๋ง์ ๊ฑธ ์ป์ด๊ฐ๋ ๊ฒ ๊ฐ๋ค.
๋ด์ผ๋ถํฐ ์๋ก์ด ๋ฏธ์ ์์์ธ๋ฐ... ํ์ดํ ํด์ผ๊ฒ ๋ค! ๐ช
'์ฐ์ํํ ํฌ์ฝ์ค > ๋ ๋ฒจ 2' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[์ฐํ ์ฝ 5๊ธฐ] ๋ ๋ฒจ 2 ๋ ๋ฒจ ์ธํฐ๋ทฐ ์ ๋ฆฌ ๋ฐ ๊ฐ์ ํ๊ณ (5) | 2023.06.16 |
---|---|
[์ฐํ ์ฝ 5๊ธฐ] ์ฅ๋ฐ๊ตฌ๋ ํ์ ๋ฏธ์ ํ๊ณ (2) | 2023.06.16 |
[์ฐํ ์ฝ 5๊ธฐ] ์ฅ๋ฐ๊ตฌ๋ ๋ฏธ์ ํ๊ณ (0) | 2023.05.15 |
[์ฐํ ์ฝ 5๊ธฐ] ์น ์๋์ฐจ ๊ฒฝ์ฃผ ๋ฏธ์ ํ๊ณ (2) | 2023.04.20 |