1. 들어가며: "공식 문서는 외계어인가요?"
처음 개발을 배울 때 가장 막막한 순간은 **"공식 문서(Official Docs)를 보고 구현하세요"**라는 말을 들을 때입니다. 문서를 열어보면 모르는 용어 투성이고, 예제 코드는 내가 배운 거랑 달라서 당황스럽기만 하죠.
이번 포스팅에서는 업비트(Upbit) API를 연동하는 과정을 통해, 난생처음 보는 API 문서를 어떻게 읽고, 내가 사용하는 언어(Spring Boot)로 번역해내는지 그 상세한 사고 과정을 공유해보려 합니다.
2. 미션: "비트코인 가격을 알아오자"
우리의 목표는 단순합니다. **"지금 비트코인 얼마야?"**를 서버가 알아오는 것입니다. 업비트 개발자 센터에 접속해서 가장 먼저 해야 할 일은 **"내게 필요한 메뉴 찾기"**입니다.

2-1. 왼쪽 메뉴 정복하기: "나는 어디로 가야 하죠?"
업비트 개발자 센터의 왼쪽 메뉴를 보면 크게 4가지 키워드가 눈에 띕니다. 개발이 처음이라면 여기서부터 막막할 수 있습니다. 하나씩 해석해 봅시다.
(1) QUOTATION API (시세 조회) ✅ 우리의 선택!
- 특징: 로그인 없이 누구나 볼 수 있는 공개 데이터입니다.
- 내용: "지금 비트코인 얼마야?", "어제 거래량은 얼마였어?" 같은 시장 정보를 제공합니다.
- Ticker(현재가): 주식이나 코인 시장에서 전광판에 깜빡이는 **'현재 가격'**을 뜻하는 전문 용어입니다. 우리는 "지금 가격"을 알고 싶으니 이곳이 정답입니다.
(2) EXCHANGE API (거래 및 자산)
- 특징: **로그인(API Key)**이 필수입니다. 내 돈을 건드리는 민감한 기능입니다.
- 내용: "내 통장에 얼마 있어?", "지금 매수 주문 넣어줘" 같은 기능을 제공합니다. (나중에 '주문하기' 기능을 만들 때 사용하게 될 겁니다.)
(3) WEBSOCKET (실시간 연결)
- 특징: 한 번 연결해두면 서버가 실시간으로 데이터를 계속 쏴주는 방식입니다.
- 비유:
- REST API (Quotation): 내가 필요할 때마다 전화를 걸어서 "지금 얼마야?" 묻고 끊는 것. (일회성)
- WebSocket: 전화를 계속 켜두고 시세가 변할 때마다 수화기 너머로 "올랐어! 내렸어!" 듣는 것. (지속성)
- 왜 지금 안 쓰나요? 현재 단계에서는 "데이터를 가져와서 DB에 저장하는 기본 로직"을 구현하는 것이 목표이므로, 구현이 훨씬 직관적이고 쉬운 REST API(Quotation)를 먼저 사용합니다.
(4) DEPRECATED (사용 중단 예정) ⚠️
- 의미: "이 기능은 낡았으니 쓰지 마세요. 조만간 없앨 겁니다."라는 경고판입니다.
- 행동 요령: 이 메뉴 아래에 있는 기능들은 절대 사용하면 안 됩니다. 나중에 기껏 만들어놨는데 API가 사라져서 코드가 먹통이 될 수 있습니다.
👉 결론: 우리는 '공개된(Quotation)' **'현재 가격(Ticker)'**을 **'일회성(REST API)'**으로 조회할 것이므로, QUOTATION API -> 시세 Ticker 메뉴를 클릭합니다.
3. 난관 봉착: "OkHttp가 뭐죠?"
시세 Ticker 페이지에 들어갔더니, 오른쪽 예제 코드에 낯선 단어가 보입니다.

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder() ...
초보 개발자의 흔한 착각:
"아, API를 쓰려면 OkHttpClient라는 걸 무조건 깔아야 하는구나!"
진실:
"아니요, 저건 업비트가 보여주는 하나의 예시일 뿐입니다."
- OkHttp란? 자바에서 HTTP 요청을 쉽게 보내게 도와주는 유명한 라이브러리입니다. 마치 요리책에서 "가스불에 3분 끓이세요"라고 한 것과 같습니다.
- 우리의 상황: 우리는 Spring Boot를 쓰고 있습니다. 스프링에는 이미 강력한 **인덕션(WebClient)**이 내장되어 있습니다. 굳이 가스버너(OkHttp)를 새로 사 올 필요가 없다는 뜻입니다.
그래서 우리는 저 코드를 복사하는 게 아니라, 저 코드 속에 숨겨진 **"요청 정보(주소, 방식)"**만 쏙쏙 뽑아내서 WebClient로 재조립할 것입니다.
4. 도구 소개: WebClient 완전 정복
그럼 우리가 사용할 도구, WebClient는 도대체 무엇이고, 왜 써야 할까요? 예제 코드에 나오는 .get(), .retrieve(), .bodyToFlux() 같은 명령어들이 낯설게 느껴질 수 있습니다. 하나씩 해부해 봅시다.
4-0. 도구의 정체: WebClient란 무엇인가?
본격적인 구현에 앞서, 우리가 사용할 무기인 **WebClient**에 대해 확실히 짚고 넘어갑시다. 단순히 "좋으니까 쓴다"가 아니라, "이게 무엇이고 왜 필요한지"를 아는 것이 중요합니다.
1) 사전적 정의 (Definition)
WebClient는 Spring Framework 5.0부터 도입된 Spring WebFlux 모듈의 일부로, Non-Blocking(비동기) 방식을 지원하는 최신 HTTP 클라이언트입니다.
쉽게 말해 **"자바 코드로 만든 웹 브라우저"**입니다. 우리가 크롬 주소창에 URL을 치고 엔터를 누르듯, 자바 코드로 외부 서버에 요청을 보내고 응답을 받아오는 역할을 합니다.
4-1. 왜 RestTemplate을 버리고 WebClient를 쓰나요?
스프링에는 원래 RestTemplate이라는 아주 유명한 통신 도구가 있었습니다. 하지만 최신 스프링(5.0 이후)부터는 **WebClient**를 쓰라고 강력하게 권장합니다. 그 이유는 **"일하는 방식"**이 다르기 때문입니다.
- RestTemplate (Blocking - 동기):
- 식당에서 주문하고 음식이 나올 때까지 카운터 앞에서 멍하니 서서 기다리는 것과 같습니다.
- 응답이 올 때까지 서버가 다른 일을 못 하고 멈춰있기 때문에 비효율적입니다.
- WebClient (Non-Blocking - 비동기):
- 식당에서 주문하고 진동벨을 받은 뒤, 자리에 앉아서 유튜브도 보고 친구랑 떠드는 것과 같습니다.
- 요청만 보내놓고 서버는 다른 일을 할 수 있습니다. 결과가 오면 그때 "띠링!" 하고 처리합니다. 성능이 압도적으로 좋습니다.
4-2. WebClient 사용법: "문장 만들기"
WebClient의 코드는 마치 **영어 문장을 만드는 것(Method Chaining)**처럼 이어집니다.
WebClient.create() // 1. 야, 웹클라이언트 준비해.
.get() // 2. GET 방식으로 요청할 거야.
.uri("https://...") // 3. 주소는 여기야.
.retrieve() // 4. 자, 이제 가서 받아와! (전송)
.bodyToMono(...) // 5. 받아온 건 이 그릇에 담아줘.
.block(); // 6. 결과 나올 때까지 기다릴게. (테스트용)
4-3. 핵심 명령어 (Voca) 정리
WebClient는 메서드 체이닝(Method Chaining) 방식을 사용하여, 마치 문장을 쓰듯이 코드를 작성합니다. 자주 사용하는 핵심 명령어들을 정리했습니다.
| 단계 | 명령어(Method) | 설명 | 비유 |
| 1. 생성 | WebClient.create() | 기본 설정으로 WebClient를 생성합니다. | 브라우저 켜기 |
| WebClient.builder() | 타임아웃, 헤더 등 세부 설정을 하며 생성합니다. | 브라우저 설정 열기 | |
| 2. 방식 | .get() | 데이터를 조회할 때 사용합니다. (HTTP GET) | 읽기 모드 |
| .post() | 데이터를 저장/전송할 때 사용합니다. (HTTP POST) | 쓰기 모드 | |
| 3. 주소 | .uri(String url) | 요청을 보낼 목적지 URL을 입력합니다. | 주소창 입력 |
| 4. 전송 | .retrieve() | 요청을 서버로 발사하고 응답을 기다립니다. | 엔터 키 누르기 |
| 5. 변환 | .bodyToMono(Class) | 응답 본문을 **객체 하나(0~1개)**로 변환합니다. | 작은 택배 상자 |
| .bodyToFlux(Class) | 응답 본문을 **리스트(0~N개)**로 변환합니다. | 컨테이너 트럭 | |
| 6. 대기 | .block() | (테스트용) 비동기 처리를 강제로 동기(Blocking)로 바꿉니다. 결과가 나올 때까지 기다립니다. | 강제 대기 |
4-4. 가장 헷갈리는 개념: Mono vs Flux 🔥
WebClient를 처음 쓸 때 가장 헷갈리는 것이 **"언제 Mono를 쓰고, 언제 Flux를 쓰냐?"**입니다. 이건 **"택배 상자에 물건이 몇 개 들어있냐"**로 구분하면 쉽습니다.
- 📦 .bodyToMono(Class) (Mono = 1)
- 의미: 결과가 0개 아니면 1개일 때 씁니다.
- 예시: "내 정보 조회", "주문 1건 상세 조회" (객체 1개)
- 컨테이너 트럭 🚛 .bodyToFlux(Class) (Flux = N)
- 의미: 결과가 **0개부터 N개(여러 개)**일 때 씁니다. (List와 비슷)
- 예시: "상품 목록 조회", "주식 시세 리스트"
- 우리의 상황: 업비트 API 문서를 보니 응답이 [ {..}, {..} ] 처럼 대괄호(배열)로 묶여오죠? 여러 개니까 Flux를 써야 합니다.
5. 실전! 문서 해독 3단계 (번역하기)
API 문서를 코드로 옮기는 과정은 딱 3단계만 기억하면 됩니다.
Step 1. WHERE & HOW (어디로 보낼까?)
문서 맨 위를 보면 초록색 박스와 URL이 있습니다.

이것을 WebClient 언어로 번역하면 이렇게 됩니다.
WebClient.create()
.get() // 문서의 'GET'
.uri("https://api.upbit.com/v1/ticker...") // 문서의 'URL'
Step 2. WHAT (무엇을 보낼까?)

주소만 알면 끝이 아닙니다. "어떤 코인 줄까?"를 말해줘야겠죠? 문서 아래 Request Parameters 표를 봅니다.
- markets (필수): 반점(,)으로 구분된 종목 코드 (예: KRW-BTC)
GET 요청에서 파라미터는 보통 주소 뒤에 물음표(?)를 붙이고 적습니다.
// URL 뒤에 ?markets=KRW-BTC 라고 붙여서 "비트코인 가격 줘!"라고 말합니다.
String url = "https://api.upbit.com/v1/ticker?markets=KRW-BTC";
Step 3. RETURN (무엇이 돌아올까?)
요청을 보냈으면 받을 그릇을 준비해야 합니다. Response 예시(JSON)를 봅니다.

[
{
"market": "KRW-BTC",
"trade_price": 50000000.0,
...
}
]
여기서 두 가지 힌트를 얻습니다.
- 대괄호 [ ]: "아, 데이터가 하나가 아니라 **리스트(목록)**로 오는구나." -> WebClient의 bodyToFlux (리스트 받는 기능)를 써야겠다.
- 필드명: trade_price라는 이름으로 가격이 오는구나. -> 내 자바 객체(DTO)에도 똑같은 이름의 변수를 만들어야겠다.
6. 최종 코드 완성
위의 분석 과정을 거쳐 완성된 코드는 다음과 같습니다.
[데이터를 담을 그릇 (DTO)]
@Getter
@NoArgsConstructor
public class UpbitTickerDto {
private String market; // 문서의 "market"과 철자 일치!
private Double trade_price; // 문서의 "trade_price"와 철자 일치!
}
[데이터를 가져오는 로직 (Service)]
public void getStockPrice() {
// 1. 문서에서 찾은 URL + 파라미터
String url = "https://api.upbit.com/v1/ticker?markets=KRW-BTC";
WebClient.create()
.get() // 2. 문서의 GET 방식
.uri(url)
.retrieve()
.bodyToFlux(UpbitTickerDto.class) // 3. 리스트 형태 응답 받기
.subscribe(dto -> {
System.out.println("현재 가격: " + dto.getTrade_price());
});
}
7. 마치며
처음엔 OkHttp 예제 코드만 보고 "이걸 다 베껴야 하나?" 겁먹었지만, 사실 우리가 필요한 건 URL, 요청 방식(GET), 파라미터, 응답 형태 네 가지뿐이었습니다.
이 핵심 정보만 문서에서 쏙쏙 뽑아낼 수 있다면, WebClient든 다른 어떤 도구든 자유자재로 사용하여 API를 연동할 수 있습니다. 앞으로는 예제 코드에 압도되지 말고, **"그래서 주소가 뭔데? 뭘 보내야 하는데?"**를 먼저 찾는 습관을 들여보세요!
'개발 공부 > 프로젝트' 카테고리의 다른 글
| [사이드 프로젝트] 가상 주식 거래소 만들기 (6) - Thymeleaf를 활용한 시세 화면 구현과 SSR (0) | 2025.12.08 |
|---|---|
| [사이드 프로젝트] 가상 주식 거래소 만들기 (5) - 스케줄러를 이용한 데이터 자동화와 REST API 구현 (0) | 2025.12.02 |
| [사이드 프로젝트] 가상 주식 거래소 만들기 (4) - WebClient를 활용한 실시간 시세 수집과 데이터 동기화 (0) | 2025.12.01 |
| [사이드 프로젝트] 가상 주식 거래소 만들기 (3) - 비즈니스 로직 구현과 서비스 계층 테스트 (1) | 2025.11.25 |
| [사이드 프로젝트] 가상 주식 거래소 만들기 (2) - 도메인 설계와 JPA Entity의 기술적 고민들 (0) | 2025.11.24 |