안녕하세요? 도정진입니다.
년초에 사용하던 프로테크님 계산기가 어떤 사정이 있었는지는 모르지만, 더 이상 운영을 하지 않는다고 하셔서, 큰 일이 생기게 되었는데요.
일단, 계산기를 하나 만들기로 하였습니다.
이 계산기는 1월 4일 부터 구동되기 시작했고 지금까지 운영되고 있으며, 더 이상 기능을 추가할 계획은 없기 때문에 한 프로젝트를 마친다고 생각하고 글을 작성합니다.
그 계산기는 아래 주소입니다.
https://themore.djjproject.com/
제 이름을 다서 도모아 계산기라고 이름을 지어보았습니다.
1. 개발 역량
일단 저는 웹쪽을 잘 모릅니다. 어쩌면 임베디드를 좋아하고 리눅스 서버쪽 부분만 작업을 해본 경험이 대다수이기 때문에 웹을 1도 모른다고 봐도 무방할 것 같습니다.
웹서버 구축이나 DB설정 등은 자신있지만 어떤 기능을 하는 페이지를 만들라고 하면 아예 하지 못합니다.
언어의 경우 JAVA로 코딩테스트를 치뤄왔으나, 지금 C로만.. 근 4년째 임베디드를 하면서 거의 아예 JAVA에 대한 문법이 기억나지 않습니다. 다만, 자동화를 위해 python 정도는 능숙하게 할 수 있는 상황입니다.
그 python도 심도있는 작성은 하지 못하는 상황이나 대략적으로 구현을 할 수 있는 상황에 있습니다.
2. 개발 스택 선택 및 구조
일단 제일 만만하게 flask 와 웹쪽은 아무 생각없이 사용하는 bootstrap 을 사용하기로 했습니다.
그럼 그림으로는 아래와 같은 구조가 될 것 같습니다.
1) 웹 페이지
flask 모듈을 사용하여 웹 백엔드를 구동하고 해당 서비스는 nginx 프록시와 연동됩니다.
그리고 제 도메인은 cloudflare 네임서버와 연동됩니다.
2) 계산기 데이터 조회
계산기 데이터는 db나 api 콜을 사용하면 좋겠지만 간단한 프로젝트여서 파일 공유를 사용하기로 했습니다. 프론트에서 특정 요청이 오면 flask에 구현된 코드가 계산기 데이터 파일을 읽어 리턴하는 방식으로 구현하였습니다.
3) 예약 실행
신한은행의 고시환율이 나오고 난 이후에 모든 통화에 대해 계산을 수행해야합니다.
cron 작업을 추가하면 아래와 같은 그림이 됩니다.
flask 로 구동되는 웹 백앤드의 경우 24시간 구동되며, 계산기만 9시 정각에 실행 됩니다.
4) API 콜
계산기 Web 은 처음에 따로 만들 계획이 없었습니다. API Call 에 대해서만 구현하려고 하였으나, 계산기 Webpage 도 있으면 괜찮을 것 같아 구현하게 된 것입니다.
api 구조는 아래와 같이 대략적으로 생각하였습니다.
[GET]
https://themore.djjproject.com/exrate?curr=USD&value=4.55&date=2024-01-10
위 부분은 2024년 1월 10일의 USD 환율의 4.55달러 값을 가져오는 것입니다.
결과는 아래와 같습니다.
{ "currency": "USD", "exrate": "1", "gentime": "24-01-15 23:43:06", "krwamount": "6103", "reqtime": "24-04-07 01:41:52", "shinhan_rate": "1324.6", "value": "4.55" } |
따로 날짜를 기입하지 않으면, 오늘 날짜 환율을 가져오게 됩니다.
이때, 각 나라마다 시간이 다른데 대응은 어떻게 할지 고민을 좀 하였는데요. 실은 이 환율 계산기의 특수성에의해 결제해야할 시간이 정해져 있어서 따로 Timezone 고려는 하지 않아도 되었습니다.
만약에 환율 데이터가 없는 상태라면 404 return 을 주도록 구현하였습니다.
다음으로 환율 테이블을 가져오는 것입니다. 각 구간별로 각 통화 전체를 가져오는 부분입니다.
[GET]
https://themore.djjproject.com/table?date=2024-01-05
{"time": "24-01-15 23:40:09", "exrate": "1322.7", "USD": {"0": {"amount": "4.48", "krwamount": "5988"}, "exrate": "1", "1": {"amount": "5.23", "krwamount": "6995"}, "2": {"amount": "5.97", "krwamount": "7989"}, "3": {"amount": "6.72", "krwamount": "8997"}, "4": {"amount": "7.46", "krwamount": "9990"}, "5": {"amount": "8.21", "krwamount": "10997"}, "6": {"amount": "8.96", "krwamount": "11991"}, "7": {"amount": "9.71", "krwamount": "12998"}, "8": {"amount": "10.45", "krwamount": "13992"}, "9": {"amount": "11.20", "krwamount": "14998"}}, "JPY": {"exrate": 0.0070008331, "0": {"amount": "640", "krwamount": "5988"}, "1": {"amount": "747", "krwamount": "6995"}, "2": {"amount": "853", "krwamount": "7989"}, "3": {"amount": "960", "krwamount": "8997"}, "4": {"amount": "1066", "krwamount": "9990"}, "5": {"amount": "1173", "krwamount": "10997"}, "6": {"amount": "1280", "krwamount": "11991"}, "7": {"amount": "1387", "krwamount": "12998"}, "8": {"amount": "1493", "krwamount": "13992"}, "9": {"amount": "1600", "krwamount": "14998"}}, |
GET 메소드로는 2048char 만큼의 리턴을 보낼 수 있습니다.
따라서 위 테이블의 경우, 2048을 훨씬 초과하는 부분이기 때문에 HTML 문서로 따로 전달하도록 구현하였습니다.
3. 계산기 구현
일단 계산기의 경우 아래의 부분이 필요합니다.
1. 신한은행의 고시환율 가져오기
2. 비자 환율 가져오기
3. 환율 계산식
4. 코드로 반영
일단 차례대로 확인하겠습니다.
3-1. 신한은행 고시환율 가져오기
일단 이 부분은 다른 계산기에서는 여러가지 방법을 사용하는 것으로 알고 있습니다.
직접 신한은행 고시환율 사이트에서 환율을 API 요청으로 가져온다거나 하는데, 당시에 API Call 에 대한 이해가 부족하여 크롤링으로 구현을 한 상태입니다.
특히, 주말 / 공휴일에는 고시환율이 변하지 않는데 이 부분도 웹 크롤링으로 하면 단숨에 해결되기 때문입니다.
단, 과거 환율을 생성할때는 좀 문제가 있습니다. 이를 크롤링으로 구현하면 값을 가져오는데에 많은 시간이 소요되기 때문입니다.
네이버 고시환율을 참고하는 방법도 있다고는 하는데 따로 확인해보지는 않았습니다.
https://bank.shinhan.com/index.jsp#020501010200
필요한 값은 위 환율 페이지에서 아래 1회차 고시환율의 송금 보낼때 환율입니다.
금요일 1회차 고시환율이 그대로 토요일 일요일까지 적용됩니다.
주말 공휴일에는 따로 환율이 고시되지 않기 때문입니다.
현 글을 쓰는 시간에는 계산기가 환율 데이터를 가지고 있지 않아 어제 환율과 금요일 환율을 확인해보면 값이 동일함을 알 수 있습니다.
3-2. 비자환율 가져오기
실제로 VISA 카드를 사용하면 사용구조가 아래와 같습니다.
해외통화 --> VISA 환율기준으로 USD로 변경 --> 카드사 매입 |
이렇게 되기 때문에 USD 통화 제외하고는 무조껀 비자내부 환율정보가 필요합니다.
카드사 매입될 때에는 무조껀 USD로 매입되기 때문입니다.
예를 들어, 24년 4월 6일 기준 JPY 1000에 대한 비자 USD 환율은 아래와 같습니다.
https://www.visakorea.com/support/consumer/travel-support/exchange-rate-calculator.html
대략 6.63USD 입니다.
이 금액이 신한카드로 청구되고, 각종 수수료가 더해집니다.
수수료까지 더한 값에 신한 고시환율로 KRW로 변환되어 결제금액이 청구되는 구조입니다.
이 부분은 따로 VISA 개발자 사이트에 API가 존재하는 상태이며, 저는 이걸 적용하는데에 너무 어려움이 있어, 프로테크님이 알려주신 방법을 사용하고 있습니다.
https://www.visakorea.com/cmsapi/fx/rates?amount=641&fee=0&utcConvertedDate=01%2F01%2F2024&exchangedate=01%2F01%2F2024&fromCurr=USD&toCurr=JPY |
위의 주소에 리턴은 아래와 같습니다.
{"originalValues":{"fromCurrency":"JPY","fromCurrencyName":"Japanese Yen","toCurrency":"USD","toCurrencyName":"United States Dollar","asOfDate":1704067200,"fromAmount":"641","toAmountWithVisaRate":"4.552876","toAmountWithAdditionalFee":"4.552876","fxRateVisa":"0.0071027701","fxRateWithAdditionalFee":"0.0071027701","lastUpdatedVisaRate":1703893827,"benchmarks":[{"benchmarkSystem":"ECB","benchmarkBaseCurrency":"EUR","benchmarkBaseCurrencyName":"Euro","toAmountWithBenchmarkRate":"4.530832","markupWithoutAdditionalFee":"0.004865","markupWithAdditionalFee":"0.004865","benchmarkFxRate":"0.0070683810","lastUpdatedBenchmarkRate":1703862326}]},"conversionAmountValue":"641","conversionBankFee":"0.0","conversionInputDate":"01/01/2024","conversionFromCurrency":"USD","conversionToCurrency":"JPY","fromCurrencyName":"Japanese Yen","toCurrencyName":"United States Dollar","convertedAmount":"4.552876","benchMarkAmount":"0.49","fxRateWithAdditionalFee":"0.0071027701","reverseAmount":"140.790140","disclaimerDate":"January 1, 2024","status":"success"} |
3-3. 환율 계산식
계산식을 정확하게 설명하진 못하지만 아래와 같이 대략적으로 계산하고 있습니다.
오늘기준환율: 1346.8 해외이용금액: 4.4$ 비자수수료 1.1%: 4.4*0.011 = 0.0484 해외접수금액(비자환율): 4.4484$ --> 4.44$ 해외이용수수료 0.18%: 4.4*0.0018 = 0.00792$ 비자매입환율: 4.44*1346.8 = 5979.792 해외이용수수료: 0.00792*1346.8 = 10.666 5989원 |
3-4. 코드 작성
코드를 python으로 작성하였고, 현재 폴더구조는 아래와 같습니다.
root@debian:/opt/themore/exrate# ├── exrate-24-03-26.txt ├── exrate-24-03-27.txt ├── exrate-24-03-28.txt ├── exrate-24-03-29.txt ├── exrate-24-03-30.txt ├── exrate-24-03-31.txt ├── exrate-24-04-01.txt ├── exrate-24-04-02.txt ├── exrate-24-04-03.txt ├── exrate-24-04-04.txt ├── exrate-24-04-05.txt ├── exrate-24-04-06.txt ├── run.sh ├── test-gen.py ├── test.py └── userdata |
계산을 한 결과를 txt 파일로 저장합니다.
이 부분을 db로 구현하면 좋았을 것 같은데, 당시에 일단 작동되는 계산기가 너무 급하여 어쩔 수 없이 적용을 하지 못하였습니다.
지금 시점에서 db로 바꾸면 되지 않는지? 이런 생각을 할 수 있는데 근 3개월동안 아무 문제 없이 구동이 되고 있어서 따로 수정을 가하고 있진 않습니다.
4. 웹 부분 작성
flask 에서 직접 web 을 표출할 수 있습니다.
3-2에서 작성한 API를 그대로 javascript 로 호출할 수 있도록 웹 페이지 작성만 하였습니다.
bootstrap 을 그대로 이용하여 구조는 아래와 같습니다.
navbar contents footer |
너무 간단합니다.
일단 아래와 같이 프로토를 만들어 보았습니다.
필요한 기능은 환율 선택 리스트 / 값을 입력하면 바로 바로 KRW가 계산될것
프로토때 사진은 아래와 같습니다.
위에 통화를 대문자로 입력하고, 해당 통화의 값을 입력하면 KRW값이 뜨는 그러한 구조였습니다.
javascript 를 거의 모르기 때문에 너무 어려웠습니다.
프로토 이후 아래 부분을 추가 구현하였습니다.
1) 환율 테이블 표시 (td / tr) / 드롭다운 셀렉터 추가
2) 환율을 드롭다운으로 선택하기 귀찮아 주요 통화 추가
3. 웹 페이지로 접근하기 보다 앱으로 처리하고 싶어 PWA 기능 추가
4) PWA 앱 설치 트리거 추가
호환이 되지 않는 브라우저가 좀 많네요.
4-1. 웹 부분 폴더구조
폴더 구조는 아래와 같이 구현되어 있습니다.
root@debian:/opt/themore/web# tree -L 2 . ├── run.sh ├── templates │ ├── ads.txt │ ├── favicon.ico │ ├── google7d0dcb9722ef5bc3.html │ ├── icons │ ├── index.html │ └── manifest.json ├── test.py └── themore-exrate-web.service |
엄청 간단합니다.
5. 도메인 비용과 서버 비용을 위한 구글 광고 부착
본래 웹을 디자인할때 광고가 보이더라도 실 사용에는 지장이 없도록 구현하였습니다.
1) 모바일뷰일때
한 화면에 필요 기능이 다 보이도록 구성하고 아래에 광고를 추가
2) PC뷰일때
좀 번잡하긴 하지만 아래의 모습이 됩니다.
3) PWA 기능을 추가한 이유
대부분 브라우저에 광고차단 기능이 추가되어 있습니다.
이를 좀 방지하기 위해 PWA 설치를 유도합니다.
6. 계산기 로직 업데이트
24년 1월1일에 MYR 통화가 의도치 않게 잘못 계산되는 증상
--> 24년 3월 초 PLN 통화가 잘못 계산되는 증상을 타 계산기에서 발견하였습니다. 제 계산기는 이미 수정이 되어 있어 정확한 계산값을 출력하였습니다.
추가적으로 해외이용수수료를 잘못 인지하여 계산하여 KRW 값이 1원 정도 차이가 나는 증상
위 부분을 수정한 적이 있습니다.
7. 마치며
생각보다 간단하게 python으로만 구현이 잘 되는 것 같습니다.
요즘은 flask 보다 fastapi를 많이 사용한다고 하는데 차후에 비슷한 스택으로 구현할 일이 생기면 그렇게 구현을 시도해보아야겠습니다.
지금 생각하면서 개발도중 너무 사안이 급해, 이틀만에 만든다고 .. 구조 생각을 하지 않고 코드를 작성한 부분이 많이 있습니다.
이렇다 보니, 조금만이라도 기능 추가나 변경이 필요하면 해당 부분에 의존하는 코드들이 있어 수정하고도 오류가 생긴적이 많이 있었습니다.
추후에는 기능을 미리 생각하고 충분히 고려한 뒤에 코드를 작성해야 좋을 것 같습니다.
이 계산기는 27년 2월 초까지 운영할 계획이며 광고수익은 도메인 연장과 서버 운영비용에 사용됩니다.
서버는 따로 AWS나 이런것들을 사용하는게 아닌 물리 서버를 활용하고 있습니다.
https://blog.djjproject.com/820
감사합니다.
댓글