- LegiNote 프로젝트 개발 이야기2 - 기술스택과 워커
- LegiNote 프로젝트 개발 이야기의 두 번째 편으로, Go 언어를 활용한 워커 개발에 대한 내용을 다룹니다. 데이터 수집 및 업데이트 로직 구현과 프로젝트 구성 방식을 소개합니다.
AI가 번역한 다른 언어 보기
durumis AI가 요약한 글
- LegiNote 프로젝트 개발 중 국회 OpenAPI를 활용하여 법률안 정보를 가져오는 작업을 진행했습니다.
- Go 언어를 사용하여 HTTP Request를 통해 데이터를 요청하고, 받은 JSON 데이터를 구조체로 파싱하는 과정을 거쳤습니다.
- API 인증키 발급부터 데이터 요청, 파싱까지의 과정을 설명하며, 향후 개선 방향을 모색하고 있습니다
안녕하세요 StatPan 입니다.
LegiNote 사이드 프로젝트의 개발기를 쓰고 있습니다.
이전편은 다음의 링크를 참고해주세요.
워커 1호 worker-bill
bill이라는 이름이 왠지 외국인 친구 이름 마냥 친숙한데요, 다음의 단어 뜻에서 영감을 얻었습니다.
적절한 변수명, 레포명을 찾기 위해서 이전에 국회 도메인과 관련된 이런 저런 영어 단어를 찾아봤는데, 그때 찾았던 것을 응용했습니다
bill 단어의 뜻
Worker가 타겟으로 데이터를 모을 곳의 OpenAPI 스펙은 다음과 같습니다
입력파라미터
법률안 심사 및 처리 API 입력인자
출력파라미터
법률안 심사 및 처리 API 입력인자
제공하는 데이터도 많고, 꽤 상세한 정보를 서비스할 수 있을 것으로 보입니다.
Http Request 준비물
우선 위 입력파라미터 Http Request 요청을 위해서 필요한 것이 무엇인지 살펴보면
URL, Http request method(여기서 나와 있지 않지만, Get 입니다 Post 안됨ㅠ), KEY, Type, pIndex, pSize 값 입니다.
KEY값의 경우, 다른 OpenAPI들과 비슷하게 별도의 인증키를 받으셔야 합니다.
개발이야기를 하는 곳이므로 레퍼런스 있으면 링크하고 넘어가려고 했는데.. 아직 마이너해서 그런지 인증키 받는 방법을 알려주는 글이 구글에 나오질 않네요... 우선 간단하게 여기서 그림 위주로 짚어 드리자면..
홈화면
홈화면에서 마이페이지 버튼을 누릅니다
마이페이지
인증키 발급 화면
위 화면에서 활용용도 또는 내용에 대해서는 저의 경우 공공데이터 대회용 목적이라고 간단하게 썼습니다. 내용을 채우는 부분에 대해서는 크게 걱정할 것 없이, 1~2줄의 불미(?)스러운 이유만 아니면 바로 승인을 해줄거라 생각합니다.
그렇게 얻은키가 바로 위 HTTP 설명에 필요한 KEY를 발급해줄 것입니다.
위 URL 요청주소에 한번 request를 날려보겠습니다(다음에 업데이트 할 땐 꼭 메소드도 포함을 해줬으면 하는 바램이)
go```go package main import ( "encoding/json" "fmt" "io" "log" "net/http" "net/url" ) func procErr(e error, msg string) { if e != nil { log.Fatal(e, msg) } } func main() { rawUrl := "https://open.assembly.go.kr/portal/openapi/TVBPMBILL11" parsedUrl, err := url.Parse(rawUrl) procErr(err, "err after parsedUrl") params := url.Values{} KEY := "your_key" //change this procErr(err, "err after req, 27") req.Header.Add("Content-Type", "application/json") req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36") params.Add("KEY", KEY) params.Add("Type", "json") // JSON 좋.아 params.Add("pIndex", "1") // 이건 왜 integer로 표시했는지 모르겠지만 string params.Add("pSize", "1") client := &http.Client{} reqURL := fmt.Sprintf("%s?%s", rawUrl, params.Encode()) req, err := http.NewRequest(http.MethodGet, reqURL, nil) res, err := client.Do(req) procErr(err, "err, after client do") defer res.Body.Close() var jsonMap any response, err := io.ReadAll(res.Body) procErr(err, "err after read response") err = json.Unmarshal(response, &jsonMap) procErr(err, "err after json unmarshal") fmt.Println(jsonMap)
```
위와 같은 코드를 사용해서 우선 동작을 확인할 수 있었습니다.
꿀팁을 하나 전달해드리자면 Header를 반드시 추가하셔야 합니다. 해당 api를 원활하게 사용하고 싶으시다면요.(반협박)
이후 Get url에 필수적인 param 들을 채워줍니다.
pIndex와 pSize의 값은 integer를 쓰라고 나와있는데, 그냥 string type을 적용하셔도 동작합니다. (이해불가)
Type에 xml 방식도 사용할 수 있는데, 저는 Json 상하차를 택했습니다
결과값
map[TVBPMBILL11:[map[head:[map[list_total_count:103305] map[RESULT:map[CODE:INFO-000 MESSAGE:정상 처리되었습니다.]]]] map[row:[map[AGE:22 BILL_ID:PRC_T2U4C0A8B2Z0A1Y3Y5G2H0F0G5E4F4 BILL_NAME:정치자금법 일부개정법률안 BILL_NO:2203179 CMT_PRESENT_DT: CMT_PROC_DT: CMT_PROC_RESULT_CD: COMMITTEE_DT: COMMITTEE_PROC_DT: CURR_COMMITTEE: CURR_COMMITTEE_ID: LAW_PRESENT_DT: LAW_PROC_DT: LAW_PROC_RESULT_CD: LAW_SUBMIT_DT: LINK_URL:https://likms.assembly.go.kr/bill/billDetail.do?billId=PRC_T2U4C0A8B2Z0A1Y3Y5G2H0F0G5E4F4 PROC_DT: PROC_RESULT_CD: PROPOSER:고동진의원 등 10인 PROPOSER_KIND:의원 PROPOSE_DT:2024-08-23 RST_MONA_CD:HS39431V RST_PROPOSER:고동진] map[AGE:22 BILL_ID:PRC_O2M4M0L8L2T0R1S3Q5R1P4Q0Y2W2X6 BILL_NAME:정당법 일부개정법률안 BILL_NO:2203178 CMT_PRESENT_DT: CMT_PROC_DT: CMT_PROC_RESULT_CD: COMMITTEE_DT: COMMITTEE_PROC_DT: CURR_COMMITTEE: CURR_COMMITTEE_ID: LAW_PRESENT_DT: LAW_PROC_DT: LAW_PROC_RESULT_CD: LAW_SUBMIT_DT: LINK_URL:https://likms.assembly.go.kr/bill/billDetail.do?...
위와 같은 결과값을 얻을 수 있습니다.
저는 별도로 저 객체를 직접 컨트롤하기 위해서 모든 struct를 정의해서 값을 가져오는데, 이거 또한 수많은 디버깅을 거쳤네요
해당값을 struct로 Unmarshal 하는 것은 글자수 제한으로 아마 다음 편에서 계속해야 할 것 같습니다.
1개 Request가 개발기 중에 한편을 다 잡아먹었네요. 물론 이 OpenAPI 사용 특성상 API의 이용과 struct 구조를 갖추는데 시간을 많이 사용하긴 했지만요. 이 부분을 다른 분들이 같은 고통을 겪지 않을 만한 방법을 찾아야겠습니다.
위 요청 중에 예상대로 동작하지 않거나 저의 이상한 코드에 불만이 생기신 분들은 담배도 챙겨 주시기 바랍니다.