목록으로

프로그래밍 · Python

Python 완전정복 시리즈 #21: requests로 공공데이터와 외부 API 가져오기

BeanCon
Python requests 라이브러리로 공공데이터와 외부 API 데이터를 수집하는 방법을 설명하는 대표 이미지

Python 완전정복 시리즈 21편입니다. requests 라이브러리 설치와 GET 요청, 응답 JSON 처리, Query Parameter와 Header, 공공데이터 API 문서 읽기, timeout, 예외 처리, 페이지네이션, 재시도, Session, JSON/CSV 저장, API Key 보호까지 실전 흐름으로 정리했습니다.

목차

데이터 사냥 원정대에 합류하셨습니다

오늘부터 여러분은 평범한 Python 학습자가 아닙니다.

인터넷 곳곳에 흩어진 데이터를 찾아오는데이터 사냥 원정대의 신입 요원입니다.

첫 번째 임무가 도착했습니다.

[긴급 임무]

서울의 날씨 데이터를 확보하라.
공공 API에서 필요한 정보만 추출하라.
실패하더라도 프로그램이 멈추지 않게 하라.
결과를 파일로 저장하라.

장비 목록을 확인합니다.

Python
requests
JSON
예외 처리
파일 입출력

지금까지 배운 기술들이 모두 한자리에 모였습니다.

그리고 이번 작전의 핵심 장비가 등장합니다.

import requests

`requests` 라이브러리입니다.

1. requests는 인터넷 탐사 장비다

Python은 기본적으로 계산, 반복, 조건 처리에 강합니다.

하지만 외부 웹사이트나 API에 직접 요청을 보내려면인터넷 통신을 담당할 도구가 필요합니다.

그 역할을 하는 대표적인 라이브러리가 `requests`입니다.

`requests`를 사용하면 Python으로 다음 작업을 할 수 있습니다.

웹 API 호출
공공데이터 가져오기
날씨 정보 조회
환율 정보 조회
뉴스 데이터 수집
AI API 호출
쇼핑몰 상품 조회
서버 상태 확인

즉, `requests`는 Python 프로그램에 인터넷 안테나를 달아주는 장비입니다. 📡

2. 작전 준비 1단계: requests 설치하기

먼저 터미널이나 명령 프롬프트에서 설치합니다.

pip install requests

설치가 완료되면 Python 코드에서 불러옵니다.

import requests

오류가 발생하지 않는다면 작전 준비 완료입니다.

3. 첫 번째 탐사: 외부 API에 GET 요청 보내기

가장 기본적인 요청은 GET입니다.

GET은 서버에서 데이터를 가져올 때 사용합니다.

import requests

url = "https://jsonplaceholder.typicode.com/posts/1"

response = requests.get(url)

print(response)

실행 결과는 다음과 비슷합니다.

<Response [200]>

여기서 `200`은 요청이 성공했다는 뜻입니다.

하지만 아직 실제 데이터는 보이지 않습니다.

4. 응답 상자를 열어보자

API 응답은 `response` 객체에 담겨 돌아옵니다.

텍스트로 확인하려면 다음과 같이 작성합니다.

print(response.text)

결과 예시

{
  "userId": 1,
  "id": 1,
  "title": "sample title",
  "body": "sample body"
}

JSON 응답을 Python 딕셔너리로 변환하려면 `json()`을 사용합니다.

data = response.json()

print(data)

이제 `data`는 Python 딕셔너리처럼 사용할 수 있습니다.

print(data["title"])

이 순간 인터넷의 JSON 데이터가 Python 변수로 변신합니다.

5. 데이터 사냥의 기본 흐름

requests를 사용하는 대부분의 코드는 다음 순서로 동작합니다.

1. 요청할 URL 준비
2. requests로 요청 전송
3. 상태 코드 확인
4. JSON 응답 변환
5. 필요한 데이터 추출
6. 오류 처리
7. 파일 또는 데이터베이스 저장

코드로 표현하면 다음과 같습니다.

import requests

url = "https://jsonplaceholder.typicode.com/posts/1"

response = requests.get(url)

if response.status_code == 200:
    data = response.json()
    print(data)
else:
    print("요청 실패:", response.status_code)

이 구조는 앞으로 API를 사용할 때 계속 반복해서 등장합니다.

6. 임무 1: 게시글 제목만 추출하라

API는 보통 많은 데이터를 반환합니다.

하지만 필요한 데이터는 일부일 수 있습니다.

import requests

url = "https://jsonplaceholder.typicode.com/posts/1"

response = requests.get(url)

if response.status_code == 200:
    data = response.json()

    title = data["title"]
    body = data["body"]

    print("제목:", title)
    print("내용:", body)

API 전체 응답을 모두 출력하는 것보다필요한 Key만 선택하는 습관이 중요합니다.

데이터 사냥꾼은 숲 전체를 들고 오지 않습니다.

필요한 열매만 따옵니다. 🌲

7. 임무 2: 여러 개의 데이터를 가져와라

이번에는 게시글 목록을 요청해보겠습니다.

import requests

url = "https://jsonplaceholder.typicode.com/posts"

response = requests.get(url)

if response.status_code == 200:
    posts = response.json()

    print(type(posts))

결과

<class 'list'>

여러 개의 데이터는 보통 리스트 형태로 반환됩니다.

반복문으로 확인할 수 있습니다.

for post in posts:
    print(post["id"], post["title"])

데이터가 너무 많다면 일부만 출력할 수도 있습니다.

for post in posts[:5]:
    print(post["id"], post["title"])

8. Query Parameter: 원하는 데이터만 주문하기

API가 수천 개의 데이터를 가지고 있다고 가정해보겠습니다.

전부 가져올 필요는 없습니다.

조건을 붙여 요청할 수 있습니다.

이를 Query Parameter라고 합니다.

직접 URL에 작성하면 다음과 같습니다.

https://jsonplaceholder.typicode.com/posts?userId=1

Python에서는 `params`를 사용하는 것이 더 좋습니다.

import requests

url = "https://jsonplaceholder.typicode.com/posts"

params = {
    "userId": 1
}

response = requests.get(
    url,
    params=params
)

print(response.url)

실제 요청 주소

https://jsonplaceholder.typicode.com/posts?userId=1

응답 데이터 확인

posts = response.json()

for post in posts:
    print(post["title"])

`params`를 사용하면 조건이 많아져도 관리하기 쉽습니다.

9. 여러 조건을 한 번에 전달하기

예를 들어 다음 조건이 있다고 가정해보겠습니다.

지역: 서울
페이지: 1
한 페이지 데이터 수: 10

Python 코드

params = {
    "city": "Seoul",
    "page": 1,
    "limit": 10
}

요청

response = requests.get(
    url,
    params=params
)

API 조건이 많을수록 문자열을 직접 조립하기보다 딕셔너리로 관리하는 것이 안전합니다.

10. Header: 신분증과 통신 규칙 전달하기

일부 API는 아무나 사용할 수 없습니다.

API Key나 Access Token이 필요합니다.

이때 Header를 사용합니다.

headers = {
    "Authorization": "Bearer ACCESS_TOKEN",
    "Content-Type": "application/json"
}

요청

response = requests.get(
    url,
    headers=headers
)

Header는 서버에게 다음 정보를 알려주는 안내문입니다.

누가 요청했는가
어떤 인증 정보를 가졌는가
어떤 데이터 형식을 사용하는가
어떤 응답 형식을 원하는가

API 세계에서 Header는 여권이자 출입증입니다.

11. 공공데이터 API는 어떻게 동작할까?

공공데이터 API는 정부나 공공기관이 제공하는 데이터를 프로그램에서 사용할 수 있도록 만든 통로입니다.

예를 들어 다음과 같은 정보를 제공할 수 있습니다.

기상 정보
대기질 정보
교통 정보
주차장 정보
병원 정보
재난 정보
관광 정보
부동산 정보

일반적인 요청 구조는 다음과 같습니다.

API 주소
서비스 키
검색 조건
페이지 번호
한 페이지 결과 수
응답 형식

예시 구조

import requests

url = "https://api.example.go.kr/weather"

params = {
    "serviceKey": "YOUR_API_KEY",
    "city": "Seoul",
    "pageNo": 1,
    "numOfRows": 10,
    "type": "json"
}

response = requests.get(
    url,
    params=params,
    timeout=5
)

실제 공공데이터 API마다 주소와 파라미터 이름은 다릅니다.

따라서 반드시 API 문서를 확인해야 합니다.

12. API 문서를 읽는 법

API 문서를 처음 보면 복잡해 보입니다.

하지만 다음 항목만 찾으면 됩니다.

요청 URL
HTTP Method
필수 파라미터
선택 파라미터
인증 방식
응답 예시
오류 코드
호출 제한

API 문서를 보며 이렇게 질문하면 됩니다.

어디로 요청하지?
무엇을 반드시 보내야 하지?
응답은 어떤 모양이지?
실패하면 어떤 코드가 오지?

문서는 보물지도입니다.

읽지 않고 출발하면 데이터 정글에서 길을 잃습니다. 🗺️

13. 임무 3: Timeout을 설정하라

서버가 항상 빠르게 응답하는 것은 아닙니다.

다음 코드는 서버를 오래 기다릴 수 있습니다.

response = requests.get(url)

실무에서는 Timeout을 설정하는 것이 좋습니다.

response = requests.get(
    url,
    timeout=5
)

5초 동안 응답이 없으면 Timeout 예외가 발생합니다.

서버가 낮잠을 자고 있는데 Python이 문 앞에서 영원히 기다리게 해서는 안 됩니다.

14. 예외 처리까지 장착한 안전한 요청

네트워크 요청은 여러 이유로 실패할 수 있습니다.

인터넷 연결 끊김
서버 응답 지연
잘못된 URL
인증 실패
서버 내부 오류

안전한 코드는 다음과 같습니다.

import requests

url = "https://jsonplaceholder.typicode.com/posts/1"

try:
    response = requests.get(
        url,
        timeout=5
    )

    response.raise_for_status()

    data = response.json()

    print(data)

except requests.exceptions.Timeout:
    print("요청 시간이 초과되었습니다.")

except requests.exceptions.ConnectionError:
    print("서버에 연결할 수 없습니다.")

except requests.exceptions.HTTPError as error:
    print("HTTP 오류:", error)

except requests.exceptions.RequestException as error:
    print("요청 처리 중 오류:", error)

이제 프로그램은 예상치 못한 상황에서도 쉽게 쓰러지지 않습니다.

15. raise_for_status()는 오류 감지 센서다

다음 요청에서 서버가 404를 반환했다고 가정해보겠습니다.

response = requests.get(url)

응답은 왔기 때문에 코드 자체는 계속 실행될 수 있습니다.

하지만 `raise_for_status()`를 사용하면 실패 상태를 예외로 바꿔줍니다.

response.raise_for_status()

다음 상태 코드에서 주로 예외가 발생합니다.

400
401
403
404
500

즉 `raise_for_status()`는 응답 상자를 열기 전에 폭발물 여부를 검사하는 센서입니다. 💣

16. 응답 데이터가 JSON이 아닐 수도 있다

모든 API가 JSON을 반환하는 것은 아닙니다.

경우에 따라 다음 형식이 올 수 있습니다.

HTML
XML
CSV
일반 텍스트

JSON이 아닐 때 `response.json()`을 실행하면 오류가 발생할 수 있습니다.

따라서 Content-Type을 확인할 수 있습니다.

content_type = response.headers.get(
    "Content-Type",
    ""
)

print(content_type)

JSON 여부 확인

if "application/json" in content_type:
    data = response.json()
else:
    print(response.text)

17. 응답 데이터가 예상과 다를 때

예상 응답

{
  "temperature": 25
}

실제 응답

{
  "message": "데이터를 찾을 수 없습니다."
}

다음 코드는 오류가 발생합니다.

print(data["temperature"])

안전한 방법

temperature = data.get("temperature")

if temperature is None:
    print("온도 데이터가 없습니다.")
else:
    print("현재 온도:", temperature)

API 응답을 무조건 믿지 않는 습관이 중요합니다.

18. 임무 4: 페이지네이션을 돌파하라

API가 10만 개의 데이터를 한 번에 보내면 서버와 클라이언트 모두 부담이 큽니다.

그래서 데이터를 여러 페이지로 나눕니다.

이를 페이지네이션(Pagination)이라고 합니다.

예시

page=1
limit=100

Python 코드

import requests

url = "https://api.example.com/items"

params = {
    "page": 1,
    "limit": 100
}

response = requests.get(
    url,
    params=params
)

여러 페이지를 반복해서 가져오려면 다음과 같이 작성할 수 있습니다.

import requests

url = "https://api.example.com/items"

all_items = []

for page in range(1, 6):
    params = {
        "page": page,
        "limit": 100
    }

    response = requests.get(
        url,
        params=params,
        timeout=5
    )

    response.raise_for_status()

    items = response.json()

    all_items.extend(items)

print("전체 데이터 수:", len(all_items))

이 코드는 1페이지부터 5페이지까지 데이터를 모읍니다.

19. 마지막 페이지는 어떻게 알까?

일부 API는 응답에 페이지 정보를 포함합니다.

{
  "page": 1,
  "totalPages": 5,
  "items": []
}

이 정보를 활용하면 마지막 페이지까지 자동으로 반복할 수 있습니다.

import requests

url = "https://api.example.com/items"

page = 1
all_items = []

while True:
    params = {
        "page": page,
        "limit": 100
    }

    response = requests.get(
        url,
        params=params,
        timeout=5
    )

    response.raise_for_status()

    result = response.json()

    items = result.get("items", [])
    total_pages = result.get("totalPages", 1)

    all_items.extend(items)

    if page >= total_pages:
        break

    page += 1

이제 데이터 사냥대가 마지막 페이지까지 자동으로 탐사합니다.

20. 호출 제한: 서버도 체력이 있다

API는 일정 시간 동안 요청할 수 있는 횟수를 제한하기도 합니다.

이를 Rate Limit이라고 합니다.

예를 들어

1분에 60회
하루에 1,000회

너무 빠르게 요청하면 다음 상태 코드가 올 수 있습니다.

429 Too Many Requests

서버의 속마음은 이렇습니다.

“한 명씩 질문하세요. 지금 모두 말하고 있습니다.”

반복 요청 사이에 잠시 쉬게 할 수 있습니다.

import time

for page in range(1, 6):
    response = requests.get(url)

    time.sleep(1)

호출 제한은 API 문서를 통해 확인해야 합니다.

21. 재시도 전략: 한 번 실패했다고 포기하지 말자

네트워크 오류는 일시적일 수 있습니다.

한 번 실패한 요청을 잠시 후 다시 시도할 수 있습니다.

import time
import requests

url = "https://api.example.com/data"

max_retries = 3

for attempt in range(1, max_retries + 1):
    try:
        response = requests.get(
            url,
            timeout=5
        )

        response.raise_for_status()

        print("요청 성공")
        break

    except requests.exceptions.RequestException:
        print(
            f"{attempt}번째 요청 실패"
        )

        if attempt == max_retries:
            print("최대 재시도 횟수를 초과했습니다.")
        else:
            time.sleep(2)

무조건 무한 재시도하는 것은 좋지 않습니다.

최대 횟수를 정해야 합니다.

22. Session: 반복 요청을 위한 전용 차량

같은 서버에 여러 번 요청할 때는 `Session`을 사용할 수 있습니다.

import requests

session = requests.Session()

session.headers.update({
    "User-Agent": "PythonDataCollector/1.0"
})

response1 = session.get(
    "https://api.example.com/items"
)

response2 = session.get(
    "https://api.example.com/users"
)

Session은 연결과 Header 설정을 재사용할 수 있습니다.

매번 새 택시를 부르는 대신 전용 차량 한 대를 운용하는 셈입니다.

23. 응답 Header에서 정보 읽기

서버는 응답 데이터 외에도 Header를 전달합니다.

print(response.headers)

특정 Header 확인

print(
    response.headers.get(
        "Content-Type"
    )
)

Rate Limit 정보가 포함되는 API도 있습니다.

remaining = response.headers.get(
    "X-RateLimit-Remaining"
)

print("남은 요청 횟수:", remaining)

단, Header 이름은 API마다 다를 수 있습니다.

24. 임무 5: 가져온 데이터를 JSON 파일로 저장하라

API 데이터를 파일로 저장해보겠습니다.

import json
import requests

url = "https://jsonplaceholder.typicode.com/posts"

response = requests.get(
    url,
    timeout=5
)

response.raise_for_status()

posts = response.json()

with open(
    "posts.json",
    "w",
    encoding="utf-8"
) as file:
    json.dump(
        posts,
        file,
        ensure_ascii=False,
        indent=4
    )

print("posts.json 저장 완료")

이제 인터넷에서 가져온 데이터가 로컬 파일에 보관됩니다.

25. CSV 파일로 저장하기

표 형태의 데이터는 CSV로 저장하기 좋습니다.

import csv
import requests

url = "https://jsonplaceholder.typicode.com/posts"

response = requests.get(
    url,
    timeout=5
)

response.raise_for_status()

posts = response.json()

with open(
    "posts.csv",
    "w",
    newline="",
    encoding="utf-8-sig"
) as file:

    writer = csv.writer(file)

    writer.writerow([
        "id",
        "userId",
        "title"
    ])

    for post in posts:
        writer.writerow([
            post["id"],
            post["userId"],
            post["title"]
        ])

print("posts.csv 저장 완료")

`utf-8-sig`는 일부 스프레드시트 프로그램에서 한글 깨짐을 줄이는 데 도움이 됩니다.

26. 실전 프로젝트: API 데이터 수집기 만들기

지금까지 배운 내용을 함수로 정리해보겠습니다.

import json
import requests


def fetch_data(url, params=None):
    try:
        response = requests.get(
            url,
            params=params,
            timeout=5
        )

        response.raise_for_status()

        return response.json()

    except requests.exceptions.Timeout:
        print("요청 시간이 초과되었습니다.")

    except requests.exceptions.ConnectionError:
        print("서버 연결에 실패했습니다.")

    except requests.exceptions.HTTPError as error:
        print("HTTP 오류:", error)

    except requests.exceptions.RequestException as error:
        print("요청 오류:", error)

    return None

파일 저장 함수

def save_json(
    filename,
    data
):
    with open(
        filename,
        "w",
        encoding="utf-8"
    ) as file:
        json.dump(
            data,
            file,
            ensure_ascii=False,
            indent=4
        )

실행

url = "https://jsonplaceholder.typicode.com/posts"

posts = fetch_data(url)

if posts is not None:
    save_json(
        "posts.json",
        posts
    )

    print(
        len(posts),
        "개의 데이터를 저장했습니다."
    )

이제 재사용 가능한 작은 데이터 수집기가 완성되었습니다.

27. 실전 프로젝트 업그레이드: 필요한 데이터만 저장하기

API 응답 전체를 저장할 필요는 없습니다.

selected_posts = []

for post in posts:
    selected_posts.append({
        "id": post["id"],
        "title": post["title"]
    })

저장

save_json(
    "selected_posts.json",
    selected_posts
)

필요한 데이터만 남기면 파일 크기도 줄고 이후 분석도 쉬워집니다.

28. 데이터 수집 로그 남기기

실무에서는 언제 어떤 요청을 했는지 기록하는 것이 중요합니다.

from datetime import datetime

now = datetime.now()

print(
    "수집 시간:",
    now.strftime(
        "%Y-%m-%d %H:%M:%S"
    )
)

파일명에 날짜를 넣을 수도 있습니다.

filename = now.strftime(
    "posts_%Y%m%d_%H%M%S.json"
)

이렇게 하면 실행할 때마다 새로운 파일이 생성됩니다.

29. 환경 변수로 API Key 보호하기

API Key를 코드에 직접 쓰면 위험합니다.

잘못된 예

API_KEY = "my-secret-key"

환경 변수에서 가져오는 방식이 좋습니다.

import os

api_key = os.getenv(
    "PUBLIC_DATA_API_KEY"
)

값 확인

if not api_key:
    raise ValueError(
        "API Key가 설정되지 않았습니다."
    )

소스 코드와 비밀 정보를 분리하는 것은 실무에서 매우 중요한 습관입니다.

30. 공공데이터 API 사용 시 자주 발생하는 문제

서비스 키 인코딩 문제

일부 API Key는 이미 URL 인코딩되어 제공될 수 있습니다.

문서에서 인코딩 키와 디코딩 키 사용 방법을 확인해야 합니다.

응답 형식이 XML로 오는 문제

JSON을 요청했는데 XML이 반환될 수 있습니다.

print(response.text)

로 실제 응답을 먼저 확인해야 합니다.

데이터가 없는데 성공 코드가 오는 문제

HTTP 상태 코드는 200이지만 실제 응답 내용에 오류 메시지가 들어 있을 수 있습니다.

{
  "resultCode": "03",
  "resultMsg": "NO DATA"
}

따라서 상태 코드뿐 아니라 내부 결과 코드도 확인해야 합니다.

호출 횟수 초과

무료 API는 일일 호출량이 제한될 수 있습니다.

요청 횟수를 기록하고 불필요한 반복 호출을 줄여야 합니다.

31. 초보자가 빠지는 함정 극장

함정 1: URL 오타

htps://

Python

주소가 이상한데요?

함정 2: timeout 없음

서버 응답 대기 중...
아직 대기 중...
계속 대기 중...

개발자

퇴근해도 될까요?

Python

아직 서버 기다리는 중입니다.

함정 3: JSON이라고 믿고 바로 변환

data = response.json()

실제 응답

<html>
  Server Error
</html>

Python

이건 JSON이 아니잖아요!

함정 4: Key가 항상 있다고 생각함

temperature = data["temperature"]

실제 데이터

{
  "message": "조회 결과 없음"
}

결과

KeyError

함정 5: API Key 공개

GitHub에 업로드

API_KEY = "실제 비밀키"

잠시 후

사용량 100% 소진
예상하지 못한 비용 발생

비밀키는 코드보다 더 조심해서 관리해야 합니다.

32. requests 실무 체크리스트

API 요청 전

URL이 정확한가?
HTTP Method가 맞는가?
필수 Parameter가 있는가?
API Key가 필요한가?
응답 형식은 JSON인가?

API 요청 중

timeout을 설정했는가?
예외 처리를 했는가?
상태 코드를 확인했는가?

API 요청 후

응답 구조를 검증했는가?
필요한 데이터만 추출했는가?
호출 제한을 확인했는가?
파일이나 DB에 안전하게 저장했는가?

33. requests를 배우면 가능한 것

requests 라이브러리를 익히면 다음과 같은 프로그램을 만들 수 있습니다.

매일 날씨를 저장하는 프로그램
공공데이터 자동 수집기
환율 조회 프로그램
뉴스 API 수집기
주식 정보 모니터링
서버 상태 점검 도구
AI 챗봇 연결 프로그램
쇼핑몰 가격 비교 시스템
보안 로그 수집기
업무 자동화 봇

Python이 인터넷 데이터를 가져오기 시작하는 순간, 만들 수 있는 프로그램의 범위가 크게 확장됩니다.

34. 지금까지 배운 기술이 다시 모였다

requests 실전 코드에는 지금까지 배운 내용이 모두 들어갑니다.

모듈

import requests

딕셔너리

params = {
    "page": 1
}

조건문

if response.status_code == 200:

반복문

for item in items:

함수

def fetch_data():

예외 처리

try:
    ...
except:
    ...

JSON

response.json()

파일 입출력

with open(...):

Python 완전정복 시리즈에서 배운 문법들이 이제 하나의 실제 프로그램으로 조립되고 있습니다.

35. 오늘 배운 내용을 한 문장으로 정리하면

`requests`는 Python 프로그램이 외부 API에 요청을 보내고, 응답 데이터를 받아 안전하게 가공하고 저장할 수 있게 해주는 대표적인 HTTP 통신 라이브러리입니다.

36. 데이터 사냥 원정대의 첫 번째 임무 완료

여러분은 이제 단순히 API를 호출하는 수준을 넘어섰습니다.

조건을 붙여 요청하고
인증 정보를 전달하고
여러 페이지를 순회하고
오류에 대응하고
필요한 데이터만 추출하고
파일로 저장할 수 있습니다.

처음에는 인터넷에서 데이터 하나를 가져오는 것도 낯설었습니다.

하지만 이제 Python은 거대한 데이터 정글을 돌아다니며 필요한 정보를 찾아오는 탐사 로봇이 되었습니다. 🤖🌐

다음 편 예고

다음 시간에는

"[Python 완전정복 시리즈 #22] 웹 크롤링 완벽 이해하기 | API가 없는 웹사이트에서 데이터를 수집하는 방법"

을 배워보겠습니다.

모든 웹사이트가 API를 제공하지는 않습니다.

그렇다면 웹페이지 안에 숨어 있는 제목, 가격, 링크, 표 데이터를 Python으로 어떻게 찾아낼 수 있을까요?

다음 편에서는

HTML 구조
BeautifulSoup
CSS 선택자
웹페이지 요청
데이터 추출
크롤링 주의사항

을 하나의 보물찾기 프로젝트로 연결해보겠습니다.

API의 정문이 닫혀 있다면, 이제 HTML이라는 거대한 건물 지도를 읽을 차례입니다.

댓글

0

댓글을 불러오는 중입니다.