-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
[feat] 지역 조회 API 구현 (#493)
- Loading branch information
Showing
22 changed files
with
792 additions
and
5 deletions.
There are no files selected for viewing
75 changes: 75 additions & 0 deletions
75
backend/src/main/java/dev/tripdraw/area/application/AreaService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package dev.tripdraw.area.application; | ||
|
||
import dev.tripdraw.area.domain.Area; | ||
import dev.tripdraw.area.domain.AreaRepository; | ||
import dev.tripdraw.area.dto.AreaReqeust; | ||
import dev.tripdraw.area.dto.AreaResponse; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@RequiredArgsConstructor | ||
@Transactional | ||
@Service | ||
public class AreaService { | ||
|
||
private final AreaRepository areaRepository; | ||
private final OpenApiAreaDownloader openAPIAreaDownloader; | ||
|
||
@Transactional(readOnly = true) | ||
public AreaResponse read(String sido, String sigungu) { | ||
if (sido.isBlank()) { | ||
return readAllSidos(); | ||
} | ||
|
||
if (sigungu.isBlank()) { | ||
return readAllSigungusOf(sido); | ||
} | ||
|
||
return readAllUmdsOf(sido, sigungu); | ||
} | ||
|
||
private AreaResponse readAllSidos() { | ||
List<Area> areas = areaRepository.findAll(); | ||
List<String> sidos = areas.stream() | ||
.map(Area::sido) | ||
.distinct() | ||
.sorted() | ||
.toList(); | ||
|
||
return AreaResponse.from(sidos); | ||
} | ||
|
||
private AreaResponse readAllSigungusOf(String sido) { | ||
List<Area> areasOfSido = areaRepository.findBySido(sido); | ||
List<String> sigungus = areasOfSido.stream() | ||
.map(Area::sigungu) | ||
.distinct() | ||
.sorted() | ||
.toList(); | ||
|
||
return AreaResponse.from(sigungus); | ||
} | ||
|
||
private AreaResponse readAllUmdsOf(String sido, String sigungu) { | ||
List<Area> areasOfSigunguAndSido = areaRepository.findBySidoAndSigungu(sido, sigungu); | ||
List<String> umds = areasOfSigunguAndSido.stream() | ||
.map(Area::umd) | ||
.distinct() | ||
.sorted() | ||
.toList(); | ||
|
||
return AreaResponse.from(umds); | ||
} | ||
|
||
public void create(List<Area> areas) { | ||
long count = areaRepository.count(); | ||
if (count != 0) { | ||
return; | ||
} | ||
|
||
areaRepository.deleteAllInBatch(); | ||
areaRepository.saveAll(areas); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
backend/src/main/java/dev/tripdraw/area/application/AreaServiceFacade.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package dev.tripdraw.area.application; | ||
|
||
import dev.tripdraw.area.domain.Area; | ||
import dev.tripdraw.area.dto.AreaReqeust; | ||
import dev.tripdraw.area.dto.AreaResponse; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.scheduling.annotation.Scheduled; | ||
import org.springframework.stereotype.Service; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class AreaServiceFacade { | ||
|
||
private final AreaService areaService; | ||
private final OpenApiAreaDownloader openApiAreaDownloader; | ||
|
||
public AreaResponse read(String sido, String sigungu) { | ||
return areaService.read(sido, sigungu); | ||
} | ||
|
||
@Scheduled(cron = "0 0 0 1 * ?") // 매달 1일에 업데이트 한다는 CRON 표현식 | ||
public void create() { | ||
List<Area> areas = openApiAreaDownloader.download(); | ||
areaService.create(areas); | ||
} | ||
} |
109 changes: 109 additions & 0 deletions
109
backend/src/main/java/dev/tripdraw/area/application/OpenApiAreaDownloader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package dev.tripdraw.area.application; | ||
|
||
import dev.tripdraw.area.domain.Area; | ||
import dev.tripdraw.area.dto.OpenApiAccessTokenResponse; | ||
import dev.tripdraw.area.dto.OpenApiAreaResponse; | ||
import dev.tripdraw.area.dto.OpenApiTotalAreaResponse; | ||
import java.net.URI; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.client.RestTemplate; | ||
import org.springframework.web.util.UriComponentsBuilder; | ||
|
||
@RequiredArgsConstructor | ||
@Component | ||
public class OpenApiAreaDownloader { | ||
|
||
private static final String AREA_API_DOMAIN = "https://sgisapi.kostat.go.kr"; | ||
private static final String AREA_CODE_NAME = "cd"; | ||
private static final String ACCESS_TOKEN_NAME = "accessToken"; | ||
private static final String SERVICE_KEY_NAME = "consumer_key"; | ||
private static final String SECRET_KEY_NAME = "consumer_secret"; | ||
private static final String AUTHENTICATION_URI = "/OpenAPI3/auth/authentication.json"; | ||
private static final String AREA_URI = "/OpenAPI3/addr/stage.json"; | ||
|
||
private final RestTemplate restTemplate; | ||
|
||
@Value("${open.api.area.service}") | ||
private String serviceSecret; | ||
|
||
@Value("${open.api.area.key}") | ||
private String keySecret; | ||
|
||
public List<Area> download() { | ||
String accessToken = requestAccessToken(); | ||
return downloadAreas(accessToken); | ||
} | ||
|
||
private String requestAccessToken() { | ||
URI authenticationUri = UriComponentsBuilder.fromUriString(AREA_API_DOMAIN) | ||
.path(AUTHENTICATION_URI) | ||
.queryParam(SERVICE_KEY_NAME, serviceSecret) | ||
.queryParam(SECRET_KEY_NAME, keySecret) | ||
.encode() | ||
.build() | ||
.toUri(); | ||
|
||
OpenApiAccessTokenResponse accessTokenResponse = restTemplate.getForObject( | ||
authenticationUri, | ||
OpenApiAccessTokenResponse.class | ||
); | ||
|
||
return accessTokenResponse.result().get(ACCESS_TOKEN_NAME); | ||
} | ||
|
||
private List<Area> downloadAreas(String accessToken) { | ||
List<Area> allAreas = new ArrayList<>(); | ||
|
||
List<OpenApiAreaResponse> sidoResponses = requestAreas(accessToken, null); | ||
for (OpenApiAreaResponse sidoResponse : sidoResponses) { | ||
fetchSigunguAndUmd(accessToken, allAreas, sidoResponse); | ||
} | ||
return allAreas; | ||
} | ||
|
||
private List<OpenApiAreaResponse> requestAreas(String accessToken, String code) { | ||
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(AREA_API_DOMAIN) | ||
.path(AREA_URI) | ||
.queryParam(ACCESS_TOKEN_NAME, accessToken); | ||
|
||
if (code != null) { | ||
uriComponentsBuilder.queryParam(AREA_CODE_NAME, code); | ||
} | ||
|
||
URI areaUri = uriComponentsBuilder.encode() | ||
.build() | ||
.toUri(); | ||
|
||
OpenApiTotalAreaResponse totalAreaResponse = restTemplate.getForObject(areaUri, OpenApiTotalAreaResponse.class); | ||
return totalAreaResponse.result(); | ||
} | ||
|
||
private void fetchSigunguAndUmd( | ||
String accessToken, | ||
List<Area> allAreas, | ||
OpenApiAreaResponse sidoResponse | ||
) { | ||
List<OpenApiAreaResponse> sigunguResponses = requestAreas(accessToken, sidoResponse.code()); | ||
for (OpenApiAreaResponse sigunguResponse : sigunguResponses) { | ||
String sigungu = sigunguResponse.address(); | ||
List<OpenApiAreaResponse> umdResponses = requestAreas(accessToken, sigunguResponse.code()); | ||
addAreas(allAreas, sidoResponse.address(), sigungu, umdResponses); | ||
} | ||
} | ||
|
||
private void addAreas( | ||
List<Area> allAreas, | ||
String sido, | ||
String sigungu, | ||
List<OpenApiAreaResponse> umdResponses | ||
) { | ||
for (OpenApiAreaResponse umdResponse : umdResponses) { | ||
String umd = umdResponse.address(); | ||
allAreas.add(new Area(sido, sigungu, umd)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package dev.tripdraw.area.domain; | ||
|
||
import static jakarta.persistence.GenerationType.IDENTITY; | ||
import static lombok.AccessLevel.PROTECTED; | ||
|
||
import dev.tripdraw.common.entity.BaseEntity; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.Id; | ||
import java.util.Objects; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.experimental.Accessors; | ||
|
||
@Accessors(fluent = true) | ||
@Getter | ||
@NoArgsConstructor(access = PROTECTED) | ||
@Entity | ||
public class Area extends BaseEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = IDENTITY) | ||
@Column(name = "area_id") | ||
private Long id; | ||
|
||
@Column(name = "sido", nullable = false) | ||
private String sido; | ||
|
||
@Column(name = "sigungu", nullable = false) | ||
private String sigungu; | ||
|
||
@Column(name = "umd", nullable = false) // 읍,면,동 | ||
private String umd; | ||
|
||
public Area(String sido, String sigungu, String umd) { | ||
this(null, sido, sigungu, umd); | ||
} | ||
|
||
public Area(Long id, String sido, String sigungu, String umd) { | ||
this.id = id; | ||
this.sido = sido; | ||
this.sigungu = sigungu; | ||
this.umd = umd; | ||
} | ||
|
||
public String toFullAddress() { | ||
return sido + " " + sigungu + " " + umd; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
Area area = (Area) o; | ||
return Objects.equals(sido, area.sido) && Objects.equals(sigungu, area.sigungu) | ||
&& Objects.equals(umd, area.umd); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(sido, sigungu, umd); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
backend/src/main/java/dev/tripdraw/area/domain/AreaRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package dev.tripdraw.area.domain; | ||
|
||
import java.util.List; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface AreaRepository extends JpaRepository<Area, Long> { | ||
|
||
List<Area> findBySido(String sido); | ||
|
||
List<Area> findBySidoAndSigungu(String sido, String sigungu); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package dev.tripdraw.area.dto; | ||
|
||
public record AreaReqeust(String sido, String sigungu) { | ||
} |
9 changes: 9 additions & 0 deletions
9
backend/src/main/java/dev/tripdraw/area/dto/AreaResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package dev.tripdraw.area.dto; | ||
|
||
import java.util.List; | ||
|
||
public record AreaResponse(List<String> areas) { | ||
public static AreaResponse from(List<String> areas) { | ||
return new AreaResponse(areas); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
backend/src/main/java/dev/tripdraw/area/dto/OpenApiAccessTokenResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package dev.tripdraw.area.dto; | ||
|
||
import java.util.Map; | ||
|
||
public record OpenApiAccessTokenResponse( | ||
String id, | ||
Map<String, String> result, | ||
String errMsg, | ||
String errCd, | ||
String trId | ||
) { | ||
} |
24 changes: 24 additions & 0 deletions
24
backend/src/main/java/dev/tripdraw/area/dto/OpenApiAreaResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package dev.tripdraw.area.dto; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
||
public record OpenApiAreaResponse( | ||
@JsonProperty("cd") | ||
String code, | ||
|
||
@JsonProperty("addr_name") | ||
String address, | ||
|
||
@JsonProperty("full_addr") | ||
String fullAddress, | ||
|
||
@JsonProperty("x_coor") | ||
String longitude, | ||
|
||
@JsonProperty("y_coor") | ||
String latitude, | ||
|
||
@JsonProperty("pg") | ||
String page | ||
) { | ||
} |
6 changes: 6 additions & 0 deletions
6
backend/src/main/java/dev/tripdraw/area/dto/OpenApiTotalAreaResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package dev.tripdraw.area.dto; | ||
|
||
import java.util.List; | ||
|
||
public record OpenApiTotalAreaResponse(String id, List<OpenApiAreaResponse> result) { | ||
} |
Oops, something went wrong.