![[백준 문제 추천/개발 일지] (2) 문제 Tag DB에 저장하기 + 회고](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRQTbc%2FbtsHNy08Jaq%2FywcKKmu2dHe6sikiP8ndE1%2Fimg.png)
백준 문제 추천
Json을 사용해서 데이터 처리를 처음 해봤다. 공식 문서를 읽고 했는 데도 데이터가 잘 안떠서 내가 만들면서 잘못한 점도 적으려고 한다. 또한 solved.ac의 api를 사용하는 만큼 호출을 적게 해야하는데 이에 대한 고민도 생각해 봐야겠다.
sovled.ac api로 Tag 받아 DB에 저장하기
간단 로직 설명
1. GET /solved/tag
public class SolvedAPIController {
private final ProblemTagService problemTagService;
//24-06-03날자상 206개의 tag존재 5page까지 가져오면 됨
@GetMapping("/tag")
public void fetchAndSaveProblemTag() throws IOException, InterruptedException {
for (int page = 0; page < 6; page++){
JsonObject tags = SolvedAPI.solvedacAPIRequest(SolvedAPI.getTag(page));
problemTagService.saveProblemTags(tags);
}
}
}
sovled.ac에는 현재 206개의 tag가 있는데 한번 page호출로 총 50개룰 불러올 수 있다.
따라서 넉넉하게 6개의 page를 가져오도록 코드를 만들었다.
2. getTag & solvedacAPIRequest
//문제 tag들 가져오기
public static String getTag(int page){
String uri = "https://solved.ac/api/v3/search/tag?query&page=" + page;
return uri;
}
getTag는 page기능을 사용하기 위해서 현제 페이지를 뜻하는 page파라미터 추가
public static JsonObject solvedacAPIRequest(String uri) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.header("x-solvedac-language", "ko")
.header("Accept", "application/json")
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
// JSON 데이터
JsonParser parser = new JsonParser();
return parser.parse(response.body()).getAsJsonObject();
}
(1)에서는 Gson형식으로 보내주는걸 택했었지만
나는 JsonObject형식으로 값을 주고 받는게 더 직관적인거 같아서 변경
3. DB에 저장하는 service로직
public class ProblemTagService {
private final ProblemTagRepository problemTagRepository;
//tag전부를 받아와서 저장하는 메소드
//item을 통해서 배열을 만들고
//item의 bojtagid로 primarykey값을 만든다.
public void saveProblemTags(JsonObject jsonObject){
JsonArray items = jsonObject.get("items").getAsJsonArray();
for (int i = 0; i < items.size(); i++){
JsonObject item = items.get(i).getAsJsonObject();
ProblemTag problemTag = new ProblemTag();
problemTag.setBojTagId(item.get("bojTagId").getAsLong());
problemTag.setKey((item.get("key").getAsString()));
JsonArray displayNames = item.getAsJsonArray("displayNames");
for (int j = 0; j < displayNames.size(); j++){
JsonObject displayName = displayNames.get(j).getAsJsonObject();
if (displayName.get("language").getAsString().equals("ko")){
problemTag.setDisplayName(displayName.get("name").getAsString());
problemTag.setDisplayNameShort(displayName.get("short").getAsString());
break;
}
}
problemTagRepository.save(problemTag);
}
}
}
애를 좀 많이 먹은 부분
solved에서 호출하면 items 배열로 묶여서 데이터가 온다.
있는데이터 값들을 받아온다.
item안에 또 언어별로 테그 이름을 다르게 해둬서 for문을 한번 더 돌리는 모습
근데 한국어 이름은 name이랑 short(짧게 바꾼 이름)이랑 거의 다 같다.
직면한 문제
1. tag의 primarykey를 bojTag로 할지 key로 할지
public class ProblemTag {
@Id
@Column(name = "boj_tag_id")
private Long bojTagId;
@Column(name = "display_name")
private String displayName;
@Column(name = "display_name_sort")
private String displayNameShort;
@Column(name = "key")
private String key;
}
백준에서는 key 를 ID값으로 사용한다.
이는 겹칠일이 없기 때문이기도 하고 찾을 때 좀더 직관적이라고 한다.
하지만 나는 돈이 없는 만큼 서버의 부하를 나노 세컨드라도 줄이고 싶어서
쿼리를 작성할 때 훨씬더 빠르게 작동할 수 있는 정수형 bojTagId를 ID값으로 골랐다.
나중에 key값을 어디에 사용할 지 모르니 확장성을 고려해서 일단 같이 저장하기로 결정
2. items가 인식이 안됨
JsonArray items = jsonObject.getAsJsonArray("items");
받아온 json데이터가 items된 리스트로 되어 있길래 items라는 object의 배열을 처리하라고 명령했으나
items라는 배열이 없다는 오류가 떳다.
아래 접은 글 형식으로 받아오게 되는데 처음에 받아온게 object이지 배열이 아니라서 key값 인식을 못하는 것 같다.
{
"count": 206,
"items": [
{
"key": "math",
"isMeta": false,
"bojTagId": 124,
"problemCount": 6212,
"displayNames": [
{
"language": "ko",
"name": "수학",
"short": "수학"
},
{
"language": "en",
"name": "mathematics",
"short": "math"
},
{
"language": "ja",
"name": "数学",
"short": "数学"
}
],
"aliases": []
},
{
"key": "implementation",
"isMeta": false,
"bojTagId": 102,
"problemCount": 5401,
"displayNames": [
{
"language": "ko",
"name": "구현",
"short": "구현"
},
{
"language": "en",
"name": "implementation",
"short": "impl"
},
{
"language": "ja",
"name": "実装",
"short": "impl"
}
],
"aliases": []
},
JsonArray items = jsonObject.get("items").getAsJsonArray();
따라서 다음과 같이 수정했다. items라는 key값을 먼저 찾고 그값들을 list로 변환했다.
그러니까 성공~
3. postgre의 db의 schema
나는 jpa에서 테이블을 직접 만드는 거보다 쿼리문을 사용해서 db table을 미리 만들어 두는 것을 선호한다.
예기치 못한 상황을 모면하기 위함이고
내가 생각한 테이블을 직관적으로 만들기에는 sql문으로 만드는 것이 더 안전하다는 판단이다.
아무튼
MySQL이랑 혼동한 나머지 DB안에 schema를 만들어 뒀는데
이를 까먹고 다른곳에도 table들을 만들어 둔 것
그래서 분명 인섬니아에서는 200답장이 왔는데 db에는 계속 저장이 안됐다.
이때 로그를 확인해봐도 당연 잘 저장 됐을 테니 아무런 에러도 없어서 벙찐 상태
아래처럼 2개의 테이블을 만들어 버렸다.
인텔리 제이 콘솔창을 보고 나서야 schema안에 테이블을 만들었었단걸 인지하고 DB url를 변경했다.
postgre에서는
db이름?currentSchema=스키마이름
요런식으로 db url를 변경하면 스키마를 사용할 수 있다.
4. API 사용시 page를 사용안해서 데이터 개수가 잘리던 문제
json파일에 count 206이런식으로 오길래 당연히 206개의 tag가 전부다 온줄 알았는 데,
db를 보니 왠걸 50개의 데이터만 왔다.
내가 json 객체를 만지다가 50개로 잘랐나 확인해보니까 그것도 아니였는데
문제는 page처리를 내가 안했더라
sovled.ac api문서에서 page랑 query를 어떻게 적어야 하는지 확인하고
내 코드를 수정해서 모든 tag를 내 db에 저장했다.
나중에 admin전용 웹을 만들어서 거기서 클릭 딸깍하면 다 할수 있도록 할 예정~~
'Project : 백준 문제 추천 서비스 > 개발일지' 카테고리의 다른 글
[백준 문제 추천/개발 일지] (5) 추천 시스템 구성 + 코드 리팩토링 (1) | 2024.07.01 |
---|---|
[백준 문제 추천/개발 일지] (4) Mapping table 중복 문제 + api호출 요청을 줄이기 위한 노력? (0) | 2024.06.13 |
[백준 문제 추천/개발 일지] (3) 문제 DB에 저장하기 + tag랑 Mapping하기 (1) | 2024.06.11 |
[백준 문제 추천/개발 일지] (1) 개발 이유 + sovled.ac API 사용하기 (0) | 2024.06.01 |
Coding, Software, Computer Science 내가 공부한 것들 잘 이해했는지, 설명할 수 있는지 적는 공간