
문제 제기 · 원화로 보면 왜 성과가 헷갈릴까

달러로 무언가를 사두면 두 가지가 동시에 움직입니다. 하나는 산 자산 자체의 값이 오르내리는 것, 다른 하나는 달러와 원화를 바꾸는 비율(환율)이 오르내리는 것입니다. 이 둘이 한 숫자로 섞이면 "내가 잘 골라서 번 건지, 그냥 달러가 비싸져서 번 건지"를 구분할 수 없게 됩니다.
실제 개발 기록에서도 같은 문제가 있었습니다. 투자 기록용 화면이 원화 총자산을 잘못 보여주고 있었고, 그 원인을 파고들어 보니 '환율'이라는 같은 단어를 서로 다른 목적에 뭉뚱그려 쓰고 있었다는 데 있었습니다. 이 글은 그 문제를 어떻게 확인하고 어떤 순서로 고쳤는지를, 달러 자산을 굴리는 개인 투자자·기록 도구를 만드는 사람이 자기 환경에 바로 적용할 수 있게 일반화해 정리한 것입니다.
결론을 먼저 말하면, 성과는 달러 기준으로, 원화 금액은 참고용으로 따로 보여주도록 나눴습니다. 어느 하나를 없애는 게 아니라 두 숫자의 역할을 분리하는 것이 핵심입니다.
왜 위험한가 · 고정환율 하나로 뭉뚱그렸을 때

기존 화면은 원화 금액을 계산할 때 환율을 항상 1,380원으로 고정해서 썼습니다. 실제 환율은 매일 움직이는데 화면은 늘 같은 숫자를 쓰니, 원화로 표시된 총자산이 실제와 어긋났습니다.
- 실제 환율이 1,300원이든 1,450원이든 화면은 무조건 1,380원으로 환산
- 그 결과 "지금 원화로 얼마어치인가"라는 아주 기본적인 질문에 틀린 답을 표시
여기서 성급하게 반대 방향으로 가면 더 나쁜 함정에 빠집니다. "그럼 매번 실시간 환율을 그대로 갖다 쓰면 되잖아"라고 생각하기 쉽지만, 성과를 재는 기준 금액까지 환율에 따라 흔들리면 실력으로 번 것과 환율 덕에 번 것이 뒤섞입니다. 예를 들어 달러 자산은 그대로인데 환율만 1,380원에서 1,450원으로 올라도, 실환율을 기준에 쓰면 원화 성과가 저절로 좋아진 것처럼 보입니다. 반대로 환율이 떨어지면 잘 굴렸는데도 성과가 나빠 보입니다. 이렇게 되면 다음 판단을 잘못 내리게 됩니다.
즉 위험은 두 갈래입니다. 고정환율만 쓰면 원화 평가액이 틀리고, 실환율을 기준에까지 쓰면 성과 판단이 오염됩니다. 하나의 환율로 두 목적을 다 처리하려다 생긴 문제입니다.
핵심 원인 · 표시용 환율과 기준 환율은 목적이 다르다
같은 '환율'이라는 단어를 쓰지만 쓰임새가 둘로 나뉩니다. 이 구분을 표로 먼저 정리합니다.
| 표시용 환율 | "지금 원화로 얼마인지" 보여주기 | 최신(실시간) 환율 |
| 기준 환율 | 성과가 좋은지 나쁜지 재기 | 고정된 기준 환율 |
표시용은 '지금 이 순간 현금화하면 대략 얼마'라는 감을 주는 참고값이므로 최신 환율이 맞습니다. 반대로 기준 환율은 성과의 잣대이므로 흔들리지 않아야 실력만 재집니다. 이 둘을 한 숫자로 합치면 어느 목적도 제대로 만족시키지 못합니다. 그래서 표시용과 회계 기준을 회계 관점에서 나눠야 한다고 판단한 것이 이번 변경의 출발점입니다.
확인 절차와 해결 · 환율을 가져오는 3단계
먼저 최신 환율을 가져오는 별도 부품(app/provider/usdkrw_rate.py)을 만들었습니다. 이 부품은 환율을 구할 때 다음 순서로 단계를 낮춰가며 시도합니다. 자기 프로젝트에 옮길 때도 이 우선순위를 그대로 쓸 수 있습니다.
- 실시간: 외부 시세(달러-원)를 조회해서 사용
- 캐시: 실시간 조회가 안 되면 마지막으로 받아둔 값을 사용
- 기본값: 그것마저 없으면 미리 정해둔 기본 환율을 사용
중요한 점은 환율 값 하나만 넘기는 게 아니라, 이 값이 언제·어디서 온 것이고 추정치인지까지 함께 넘기도록 한 것입니다. 담긴 정보는 아래와 같습니다.
FxRate {
rate // 환율 값
fetched_at // 가져온 시각
source // 실시간 / 캐시 / 기본값 중 무엇인지
is_estimate // 추정치 여부
}이 구조체는 '숫자 하나만 던지지 말고 그 숫자의 출처를 함께 던져라'는 원칙을 코드로 옮긴 것입니다. source와 is_estimate가 있어야 화면에서 신뢰도를 표시할 수 있습니다.
마감 리뷰 화면(shadow_review.build_review_message)은 이 정보를 받아 두 숫자를 나눠 보여줍니다.
- 전략 총자산: 달러와 퍼센트로 (환율과 무관하게 성과만 표시)
- 원화 금액: 최신 환율로 환산한 참고값으로 (≈ 원화 평가액 …(추정) · 환율 …원/$ + 실시간/캐시/기본값 시각)
또한 실시간 조회가 아닐 때는 ⚠️ 기준환율이라고 표시해, 이 숫자를 얼마나 믿어도 되는지 보는 사람이 바로 알 수 있게 했습니다. 환율을 가져오는 부분은 외부에서 갈아 끼울 수 있게 만들어, 테스트할 때는 실제 네트워크 조회 없이 검증할 수 있도록 했습니다. 이 변경과 관련해 기록상 테스트 7건, 전체 204건 통과로 남아 있습니다(수치는 발행 시점 기준 재확인이 필요합니다).
적용 체크리스트
달러(또는 다른 외화) 자산을 원화로 함께 보여주는 화면을 만든다면 아래를 순서대로 점검하세요.
- 두 목적을 먼저 분리한다. '성과 측정'과 '지금 원화 얼마'를 같은 환율로 처리하고 있지 않은지 확인한다.
- 성과는 외화·퍼센트로 표시한다. 수익률 계산에는 흔들리지 않는 기준 환율을 쓴다.
- 원화 금액은 최신 환율 참고값으로 분리한다. '정확한 값'이 아니라 '추정치'임을 문구로 명시한다.
- 환율 조회는 실시간 → 캐시 → 기본값 순으로 낮춘다. 하나가 실패해도 화면이 멈추지 않게 한다.
- 값과 함께 출처·시각을 넘긴다. 실시간이 아니면 ⚠️ 같은 신뢰도 표시를 붙인다.
- 조회 부품을 교체 가능하게 만든다. 테스트에서 네트워크 없이 가짜 값으로 검증할 수 있어야 한다.
전후 비교
| 원화 환산 환율 | 항상 1,380원 고정 | 실시간 우선, 실패 시 캐시→기본값으로 낮춰 사용하며 출처 함께 표시 |
| 원화 총자산 표시 | 고정환율(1,380원)로 환산 | 전략 성과는 달러·퍼센트로, 원화는 최신 환율 기준 참고값으로 분리 |
| 환율 신뢰도 표시 | 없음 | 출처·시각 표기, 실시간 아니면 ⚠️ |
| 원화 금액 성격 | 정확한 값처럼 보임 | 참고용 추정치로 명시 |
가장 큰 변화는 '숫자 하나가 여러 목적을 겸하던' 상태에서 '각 숫자가 한 가지 목적만 책임지는' 상태로 바뀐 것입니다. 표시가 틀렸을 때 원인을 어느 숫자에서 찾아야 하는지도 명확해집니다.
한계와 주의사항
이 방식이 만능은 아닙니다. 잘못 적용하면 오히려 혼란이 생길 수 있습니다. 예를 들어 원화 참고값을 '추정치'라고 표시하지 않고 실시간 환율로만 환산해 크게 보여주면, 보는 사람은 여전히 그 숫자를 성과로 착각합니다. 또 캐시 값을 너무 오래된 것까지 허용하면 '실시간처럼 보이지만 며칠 전 환율'인 상태가 생겨, 출처 표시만 믿다가 오판할 수 있습니다.
외부 시세 조회의 안정성·지연·정확도는 상황에 따라 달라질 수 있으므로, 실시간 값을 항상 정확하다고 단정하지 마세요. 그래서 실시간이 아닐 때 ⚠️로 신뢰도를 드러내고, 최종 판단은 보는 사람에게 맡기는 구조가 안전합니다.
이 글에서 얻은 원칙은 하나로 정리됩니다. 표시용 환율과 회계 기준 환율은 반드시 나누고, 외부 값은 실시간→캐시→기본값으로 낮춰 쓰되 그 출처를 화면에 드러낸다는 것입니다. 이 원칙은 다른 통화나 다른 화면에도 넓혀볼 수 있습니다. 다만 캐시 허용 시간, 기본값으로 떨어졌을 때의 강조 방식 같은 세부는 각자의 환경에 맞춰 검토해야 할 부분이며, 여기서 제시한 수치는 특정 프로젝트의 기록일 뿐 투자 성과나 정확도를 보장하지 않습니다.
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
'실전 원칙' 카테고리의 다른 글
| 쇼츠 자막이 밋밋한 진짜 이유: 정지 배경의 함정과 '움직이는 글자'로 바꾸는 법 (0) | 2026.08.16 |
|---|---|
| 그 순간에만 존재하는 값을 잃지 않는 법: 진입 시점 데이터 저장 전략 (0) | 2026.08.15 |
| 자동화가 사람 개입 후 멈추는 문제: 중복 없이 다시 태우는 재개 경로 설계법 (0) | 2026.08.13 |
| 인프라를 계약으로 만들면 기능 추가가 10파일로 끝난다 — 재사용 가능한 코어 설계법 (0) | 2026.08.12 |
| AI가 만든 '성공' 판정, 부호만 맞으면 통과시키는 함정 — 최소격차·표본 가드로 막는 법 (0) | 2026.08.10 |