공공데이터 활용 API 작성
2024. 12. 6. 16:50ㆍTIL
최초 1회 실행하는 Open API를 이용하여 내 DB에 데이터 저장하기
package teamhp.alarm_open.domain.service
import com.fasterxml.jackson.dataformat.xml.XmlMapper
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate
import java.net.URI
import org.springframework.jdbc.core.JdbcTemplate
import teamhp.alarm_open.domain.dto.Performance
import teamhp.alarm_open.domain.entity.PerformanceDB
import teamhp.alarm_open.domain.model.OpenApiReposiitory
@Service
class OpenApiService(
private val restTemplate: RestTemplate,
private val openApiReposiitory: OpenApiReposiitory,
) {
fun getAllRegionPerformanceList(): String {
var pageNo:Int = 1
while(pageNo <=74){
val url =
"http://api.data.go.kr/openapi/tn_pubr_public_pblprfr_event_info_api?serviceKey=내 서비스 키&pageNo=$pageNo&numOfRows=100&type=xml"
val uri = URI(url)
val response = restTemplate.getForObject(uri, String::class.java)
val xmlResponse = response ?: return "Failed to fetch data"
val xmlMapper = XmlMapper()
val rootNode = xmlMapper.readTree(xmlResponse)
println("XML Response: $xmlResponse")
val items = rootNode["body"]?.get("items")?.get("item")
if (items == null) {
return "No items found in the response."
}
val performances = mutableListOf<PerformanceDB>()
items.forEach {
val performanceDB = PerformanceDB(
eventName = it["eventNm"]?.asText() ?: "Unknown Event", // 값이 없을 경우 기본값 처리
location = it["opar"]?.asText(),
startDate = it["eventStartDate"]?.asText(),
endDate = it["eventEndDate"]?.asText(),
startTime = it["eventStartTime"]?.asText(),
endTime = it["eventEndTime"]?.asText(),
chargeInfo = it["chrgeInfo"]?.asText(),
seatNumber = it["seatNumber"]?.asText(),
admissionFee = it["admfee"]?.asText(),
ageLimit = it["entncAge"]?.asText(),
homePageUrl = it["homepageUrl"]?.asText(),
address = it["rdnmadr"]?.asText(),
latitude = parseDoubleSafe(it["latitude"]?.asText()),
longitude = parseDoubleSafe(it["longitude"]?.asText()),
region = 200,
)
performances.add(performanceDB)
}
openApiReposiitory.saveAll(performances)
pageNo++
}
return "Data fetched and saved successfully"
}
private fun parseDoubleSafe(value: String?): Double? {
return try {
value?.takeIf { it.isNotEmpty() }?.toDouble()
} catch (e: NumberFormatException) {
null
}
}
}
현재 활성화 되어있는 행사와 앞으로 예정된 행사를 조회하는 서비스 작성
@Service
class PerformanceService(
private val queryDslOpenApi: QueryDslOpenApi
) {
fun getAllPerformanceList(): List<PerformanceDB> {
return queryDslOpenApi.findCurrentAndUpcomingEvents()
}
}
package teamhp.alarm_open.domain.model
import com.querydsl.core.types.dsl.Expressions
import org.springframework.stereotype.Repository
import teamhp.alarm_open.domain.dto.Performance
import teamhp.alarm_open.domain.entity.PerformanceDB
import teamhp.alarm_open.domain.entity.QPerformanceDB
import teamhp.alarm_open.infra.querydsl.QueryDslSupport
import java.time.LocalDate
@Repository
class QueryDslOpenApi: QueryDslSupport() {
private val qPerformance = QPerformanceDB.performanceDB
val today = LocalDate.now()
fun findCurrentAndUpcomingEvents(): List<PerformanceDB> {
return queryFactory.selectFrom(qPerformance)
.where(
Expressions.dateTemplate(
LocalDate::class.java,
"TO_DATE({0}, 'YYYY-MM-DD')", qPerformance.startDate
).loe(today)
.and(
Expressions.dateTemplate(
LocalDate::class.java,
"TO_DATE({0}, 'YYYY-MM-DD')", qPerformance.endDate
).goe(today)
)
.or(
Expressions.dateTemplate(
LocalDate::class.java,
"TO_DATE({0}, 'YYYY-MM-DD')", qPerformance.startDate
).gt(today)
)
)
.fetch()
}
}
Open API를 통해서 받아온 start_date값과 end_date값이 모두 String값으로 설정되어 있었기 때문에 그대로 String 타입을 유지하면서 날짜 비교를 하였다.
클라이언트에서 각 지역에 대한 즐겨찾기 기능을 제공하고, 해당 지역에 대한 리스트를 보여줄 것이기 때문에 지역 정보가 있는 address 컬럼에서 따로 region이라는 지역코드 컬럼을 생성하였다.
클라이언트에서 지역코드를 통해서 각 지역에 대한 데이터접근이 편해지게 설계하였다.
ALTER TABLE performance_list ADD COLUMN region integer;
UPDATE performance_list
SET region =
CASE
WHEN address LIKE '서울%' THEN 1
WHEN address LIKE '부산%' THEN 2
WHEN address LIKE '대구%' THEN 3
WHEN address LIKE '인천%' THEN 4
WHEN address LIKE '광주%' THEN 5
WHEN address LIKE '대전%' THEN 6
WHEN address LIKE '울산%' THEN 7
WHEN address LIKE '세종%' THEN 8
WHEN address LIKE '경기%' THEN 9
WHEN address LIKE '강원%' THEN 10
WHEN address LIKE '충청북도%' THEN 11
WHEN address LIKE '충북%' THEN 11
WHEN address LIKE '경상남도%' THEN 12
WHEN address LIKE '경남%' THEN 12
WHEN address LIKE '전북%' THEN 13
WHEN address LIKE '전라남도%' THEN 14
WHEN address LIKE '전남%' THEN 14
WHEN address LIKE '경상북도%' THEN 15
WHEN address LIKE '경북%' THEN 15
WHEN address LIKE '제주%' THEN 16
WHEN address LIKE '충청남도%' THEN 17
WHEN address LIKE '충남%' THEN 17
ELSE 100
END;
7천개가 넘는 데이터가 있기 때문에 성능개선을 위하여 2024년 이전의 데이터와 address가 없는 데이터를 삭제하였다.
DELETE FROM performance_list
WHERE address IS NULL OR address = '' OR address = '미정';
DELETE FROM performance_list
WHERE TO_DATE(end_date,'YYYY-MM-DD') < '2024-01-01';
DB
서버가 클라이언트로 보내는 json 데이터 형태
[
{
"eventName": "삼례문화예술촌 상설공연 운영",
"location": "삼례문화예술촌 일원",
"startDate": "2024-06-01",
"endDate": "2024-12-28",
"startTime": "14:00",
"endTime": "15:00",
"chargeInfo": "무료",
"seatNumber": "108",
"admissionFee": "",
"ageLimit": "전연령",
"homePageUrl": "http://www.samnyecav.kr/board/index.php",
"address": "전북특별자치도 완주군 삼례읍 삼례역로 81-13",
"latitude": 35.90623513,
"longitude": 127.0658535,
"region": 13,
"id": 14627
},
{
"eventName": "사라장 리사이틀",
"location": "대공연장",
"startDate": "2024-12-15",
"endDate": "2024-12-15",
"startTime": "16:00",
"endTime": "17:40",
"chargeInfo": "유료",
"seatNumber": "1103",
"admissionFee": "",
"ageLimit": "초등학생 이상 관람가",
"homePageUrl": "http://arts.iksan.go.kr",
"address": "전북특별자치도 익산시 동서로 490",
"latitude": null,
"longitude": null,
"region": 13,
"id": 14663
},
필요한 데이터는 클라이언트에서 정하여 사용하는 식으로 설계하였다.
GitHub: https://github.com/kotlin2024/alarm_open/tree/develop
'TIL' 카테고리의 다른 글
내 로컬에 있는 컨테이너 EC2로 업로드하기 (0) | 2024.12.08 |
---|---|
docker 사용해보기 (0) | 2024.12.07 |
공공API 사용 (1) | 2024.12.05 |
공공데이터 활용 프로젝트 (0) | 2024.12.04 |
Docker 설치 및 세팅 (2) | 2024.12.03 |