2026-08-14
0
56
--dangerously-skip-permissions 조아
`--dangerously-skip-permissions` Claude Code의 아주 유명한 플래그이다. 플래그명에서도 알 수 있듯이 '위험'하다. 저 플래그 없이 그냥 클로드 코드를 쓰면, 시도때도 없이 허락을 구한다. ``` Delete old pm2 processes and rename server folder This command requires approval Do you want to proceed? ❯ 1. Yes 2. Yes, and don’t ask again for: ssh -i ~/.ssh/MYKEY.pem ubuntu@MYSERVER.DOMAIN 'set -e * ❯ 3. No ``` 물론 당연히 이렇게 허락을 구하는 동작이 default여야 하는게 맞다. ## 그래도 나는 거의 항상 켜둔다 저걸 안 쓰면 AI한테 뭔가 딸깍 시키고 화장실 갔다오거나 샤워하거나 외출하는게 불가능하기 때문이다. 현실 세계에서 마이크로 컨트롤을 하지 말라는 얘기를 많이 듣는 것과 똑같은 맥락이다. 사고가 나더라도 내가 책임질 수 있는 범주라고 생각한다면 권한을 많이 주는게 생산성을 엄청나게 끌어올린다. 그래서 나는 내 개인 프로젝트들을 할 때는 아예 `.pem`키와 AWS credentials도 그냥 쓰도록 하고, 자주 쓰는 서드파티들의 MCP(Slack, Figma, Jira...)를 연동해두거나 cli(aws, gh...) 인증을 해둔다. 어쨌든 위험한 건 맞지만, Opus 5는 꽤 똑똑하고 Claude Code는 자체 하네스가 잘 되어 있어서 정말로 위험한 짓까지는 그래도 잘 안 하는 편이라고 느껴진다. 예를 들어, '자러갈테니 ~해놔' 했는데, 시킨 일 중 특정 태스크가 충분하지 않은 컨텍스트 하에서 그냥 강행할 경우 서버 장애를 일으킬 수 있는 상황이었다. 그런데 '주무시는 동안 일으킬 사고는 아닌 것 같아서 넘겼다.'고 답변했던 걸 보고 꽤나 놀랐던 경험이 있다. ## 이런 것들을 시킬 수 있다 - 내 GitHub 계정에 적당한 레포를 만들고 커밋한 뒤, AWS S3에 적당한 버킷을 만들어서 정적 호스팅하고, cloudfront invalidation 과정까지 포함된 스크립트를 짜줘. => 이걸 죄다 GitHub 접속해서, AWS Console에서 일일히 뒤져가면서 하나하나하면 상당히 손이 많이 가고 매번 레퍼런스 찾느라 귀찮다. 그냥 적당한 IAM 하나 만들고 권한 줘서 키 하나 받아놓고 AI한테 준 뒤, 그 키로 다 하라고 하면 된다. GitHub도 마찬가지. - 내 레포 A 이름을 B로 바꾸고 싶은데, 내 ec2 접속해서 폴더명 바꾸고 pm2 프로세스도 다시 새로 띄워줘. => 이것도 자주 하는 일이 아니라면 어떻게 했었는지 기억하고 찾아보는게 귀찮고, 알아도 내가 매번 putty나 터미널 켜고 들어가서 서버 헤집고 다니는 건 귀찮다. - 내 레포 n개의 GitHub Workflow에 사용되는 secret들을 ~ 형태로 통일해줘. => 이거 상상만 해도 귀찮다. 걍 시간써서 일일히 하면 되기는 하는데 너무너무너무 귀찮다. 그냥 로컬에 gh 깔아두고 인증 한 번만 하면 Claude Code가 알아서 다 해준다. - 특정 피그마 섹션 링크를 주면서 '이거 구현해. 주변 잘 둘러보면서 디자인 시스템을 잘 파악해서 사전 정의된 디자인 토큰만 사용해서 구현해. 네가 임의로 값을 새로 도입하면 안 돼.' => 예전에 한땀한땀 피그마 보면서 구현하던거 생각하면 시대가 너무 바뀌었다 ㅠ.ㅠ - 이번 세션의 작업을 GitHub PR로 제출하고, Jira 카드로 만들어서 묶어줘. => 아예 skill로 만들어서 자동화해두는 것도 좋음. - Slack 스레드 링크를 주면서 '이 스레드를 읽고 ~님이 말하는 맥락이 뭔지 간략히 요약해서 어떻게 답변하면 좋을지 생각해보자' => 이런 것까지 외주주는 건 별로인 것 같긴 한데, 기능적으로 가능하다는 것. 등등 무궁무진하다. ## 이제 새로 배울 게 별로 없다 특히 내가 요즘들어 너무 편리하다 느끼는 것은, **특정한 서드파티를 새로 배울 필요가 거의 없다는 점**이다. 그냥 AI에게 '~ 해줘'하면 알아서 내게 몇 가지만 묻고 스스로 레퍼런스를 찾아서 권한만 나에게 요청한 뒤 끝내버린다. 예전같으면 직접 다 Documentation을 찾아서 이해하고 시도하면서 학습하는 과정이 병목이었는데, 지금은 '내가 뭘 하고 싶은지, 무엇이 필요한지' 도구를 찾는 과정이나 AI가 작업 후 내게 보고하는 내용을 이해하는 과정이 병목이다. '이거 자동화할 수 있을 것 같은데?' <= 웬만하면 됨 ## 대신 결정할 게 늘었다 권한을 넘길수록 내가 직접 하는 일은 줄어드는데, 그만큼 훨씬 많은 업무를 할 수 있고 당연히도 결정할 건 늘어난다. 난 AI가 아니라 인간이니 Context Switching으로 인한 피로감도 크고, 또 극도로 신중한 모델의 경우(ex: Opus 5) 답변마다 '다만', '한가지', '뒤집습니다', '나쁩니다' 등등 부정적 피드백을 포함하는 경향이 커서, 그걸 감당하는데서 오는 피로감도 만만치 않다. 물론 그게 실제 도움이 되는 맞는 지적인 경우도 많고, 이게 앤트로픽 모델들이 똑똑하다는 인상을 주는 부분이긴 하지만, 꼭 짚고 넘어가야 할 문제는 아닌 경우도 많기 때문이다. 너무나 많은 결정을 해야하는데서 오는 피로감을 AI한테서도 느끼는데, 실제 큰 회사의 중간 의사결정자들은 얼마나 피곤할지도 새삼 느끼게 된다.
2026-07-09
0
317
Claude Code와 MCP로 바뀐 나의 개발 워크플로우
최근에 다시 일을 하고 있는데, 다행히도 AI를 상당히 잘 쓰면서 일을 하는 환경에 들어오게 된 것 같아 최근의 워크플로우를 정리해봤다. 몇 년 후에 보면 또 이게 구식으로 느껴질 수도 있겠지만, 집에서 GUI 위주로만 AI를 써오다가(e.g. Antigravity, Codex 등) 거의 모든 걸 Claude Code로 터미널에서 작업하다보니 이게 더 낫다는 느낌을 받고 있다. **워크플로우** - 일단 어떤 레포에서든 무조건 claude부터 켠다. - 개발 레포들에서 흔한 git 전략으로 개발하는 건 비슷 (`feature/*` => `dev` => `main`) - Git, Jira, Figma 등이 MCP로 연동되어 있어서 그냥 말로 부탁하면 다 해줌 - 피그마 링크를 붙여넣으며 “이거 구현해줘” - “Jira 카드 만들고 풀리퀘 만들어줘” - “이전 커밋들을 확인하고 적당히 커밋 & 푸시해줘” 등등 - 회고 레포가 따로 있어서, 거의 매 작업마다 이 회고 레포에도 뭘 한 건지 3에서 만든 Jira 카드랑 연동해서 CTO님이 만든 wrapup스킬을 써서 잘 갖춰진 형식의 세션 랩업 문서를 만들고 그걸 따로 올림 - `superpowers`, `oh-my-claudecode` 등을 잘 써서 계획/구현/리뷰 등을 여러 서브에이전트로 나누는 스킬을 사용하면 Sonnet 위주의 사용으로도 상당히 괜찮은 퀄리티의 결과물이 나옴 **편한 점** - 피그마의 디자인 토큰이 프론트엔드 모노레포 최상단의 디자인 시스템과 1:1로 매칭되어 있어서, 그냥 피그마 링크를 붙여넣고 그거대로 구현해달라고 하면 너무너무너무 잘해줌. 개발자가 해야 할 일은 다듬기 & API 연동 & QA 정도 - Claude에 주요 협업툴들과의 MCP들이 붙어 있어서, 거의 모든 걸 그냥 터미널에서 끝냄. Figma, Jira, Github 등 외부 사이트들에 들어가보는 건 정말 잘 들어갔나 확인하는 정도 좀 자유롭게 작업해도 되는 레포는 클로드 기동시 `--dangerously-skip-permissions` 플래그로 실행해서 그냥 알아서 모든 작업을 끝내도록 해두고 최종 확인만 하는 편인데, 최근 들어 MySQL 기반이었던 [여기얼마](https://real.gravex.app)의 모델링을 싸가 다 뜯어고치고 PostgreSQL 기반으로 마이그레이션중이다. 손으로 한땀한땀 한다면 최소 한 달도 더 걸릴 것 같은데, AI를 쓰면서 며칠만에 끝내고 데이터를 크롤링하는 중이다. 그나마 내가 할 일은 토큰 다 떨어져서 충전되면 resume해주는 정도 [...] 일을 시켜놓고 기다리는게 업무 시간의 대부분을 차지하다보니 점점 여러 일을 병렬로 시키게 되는 요즘이다. 만들어내는 대부분이 slop이라 할지언정, 과거에 비해 산출물의 양은 정말 확실히 어마어마하게 늘어나기는 했다. 단점은, 토큰이 다 떨어지면 손발 잘리는 느낌임 ㅠ.ㅠ
2026-07-06
0
166
[코인충 마이그레이션] with Fable 5 + superpowers
코인충은 2021년도에 만든 워낙 오래된 프로젝트이다. 스택도 그 당시로서는 최선이었으나 지금보면 완전한 레거시. 특히 SSR을 직접 구현했던게 내 개인적으로 공부는 됐지만 지금은 손도 대기 끔찍한 레거시여서 마이그레이션을 해야지 해야지 생각만 하고 있다가, Fable 5가 풀린 상황에서 superpowers를 통해 계획을 세우고 Sonnet 5를 subagent로 쓰면서 `--dangerously-skip-permissions` 하에 알아서 끝까지 작업을 완료하도록 했다. 결과적으로 마이그레이션이 상당히 맘에 드는 수준으로 이뤄져서 정말 놀라웠다. Nuxt 기반으로 거의 코드를 죄다 갈아엎은 수준이지만, 사용자 입장에선 바뀐 것을 전혀 인지하지 못할 수준으로 잘 이식됐다. **대상**: coinsect_frontend (Vue 3 Composition + Vuex + axios + 자체 구현 SSR/WAS) → coinsect_nuxt (Nuxt 4 + TanStack Query + Pinia + headless composable) **기간**: 2026-07-02 ~ 2026-07-06 (커밋 기준) **결과**: 계획 20개 태스크 전부 완료, 34 커밋, 테스트 164/164 green, 실서비스(coinsect.io) 대비 SEO 패리티 검증, 프로덕션 빌드 확인 주말이 껴 있어서 7월 2일부터 6일로 결과가 나오기는 했는데 사실상 **하루만에** 완료. --- ## 스택 전환 | | 구 (coinsect_frontend) | 신 (coinsect_nuxt) | |---|---|---| | 프레임워크 | Vue 3 + Vue CLI | Nuxt 4 | | 상태관리 | Vuex | Pinia (UI/실시간 상태) | | 서버 데이터 | Vuex action + 수제 캐시(setTimeout) | TanStack Query (SSR hydration, staleTime) | | HTTP | axios | `$fetch` (Nuxt 내장) | | SSR | 자체 Express WAS + `renderToString` + `window.INITIAL_DATA` | Nitro | | 웹소켓 | 컴포넌트마다 새 커넥션, 재연결 없음 | 거래소별 공유 싱글턴 + ref-count + 자동 재연결 | | 런타임 의존성 | axios 포함 다수 | **7개뿐**: nuxt, vue, vue-router, @tanstack/vue-query, pinia, @pinia/nuxt, dayjs | 의존성 최소화를 명시적으로 요구했다 — VueUse 같은 유틸 라이브러리도 배제하고 debounce, 이벤트 리스너, 드래그, 웹소켓 재연결까지 전부 직접 구현하도록 시켰다. --- ## 프로세스: 4단계 (이 문단은 Claude Code의 입장) ### 1단계 — 코드 파악 (병렬 탐색) 계획을 세우기 전에 구 레포를 이해해야 했다. 핵심 파일(라우터, 스토어, axios 모듈, App.vue)은 직접 정독하고, 나머지는 세 개의 탐색 에이전트를 **병렬**로 띄웠다: - 앱 인프라(모달/토스트/툴팁 시스템, 이벤트 버스, 레이아웃 셸) - 라우트/기능 인벤토리(26개 라우트 전부, 챗 서브시스템, 웹소켓 훅, 커뮤니티) - SCSS 아키텍처(전역 유틸리티 클래스 의존도, 이식 난이도) 이 조사에서 나온 핵심 판단들이 이후 전체 설계를 결정했다: - 모달 시스템(`await modal.confirm()` 한 줄로 끝나는 promise-through-store 패턴)은 **보존할 가치가 있는 좋은 설계**로 판정 — Pinia 위에 같은 인체공학으로 재구현. - SCSS는 이미 `@use` 기반, `::v-deep` 0건, 테마도 100% 네이티브 CSS 변수 — **거의 그대로 이식 가능**. Tailwind 전환은 견적만 수 주 규모로 나와서 기각. - 실제 인증 라우트가 **0개**(스캐폴딩만 존재)라는 것도 이때 발견 — 이식 대상에서 제외. ### 2단계 — 계획 수립 (`superpowers:writing-plans`) 조사 결과를 바탕으로 20개 태스크짜리 구현 계획을 작성했다. 각 태스크는 파일 경로, 인터페이스 시그니처, (인프라 태스크는) 완성 코드, 보존해야 할 동작 명세, TDD 스텝까지 포함한 자기완결적 브리프였다 — 신선한 컨텍스트의 구현자가 원본을 참고하며 재작성할 수 있도록. Global Constraints에 "의존성 최소화", "Tailwind 안 함", "죽은 코드 이식 안 함(FoolsDayPrank, TwitterTimeline, 인증 스캐폴딩)" 같은 프로젝트 전역 규칙을 명시해, 이후 모든 태스크가 암묵적으로 따르게 했다. ### 3단계 — 구현 (`superpowers:subagent-driven-development`) 태스크마다 **완전히 새 컨텍스트의 Sonnet 서브에이전트**를 디스패치했다. 각 에이전트는: 1. 브리프를 읽고 원본(구 레포) 코드를 직접 읽어가며 구현 2. 테스트 작성/실행, 실라이브 API(`api.coinsect.io`)에 대고 SSR 검증 3. 자체 리뷰 후 보고 (구현 내용, 검증 증거, 이탈/우려사항) 4. **커밋은 하지 않고 스테이징까지만** — 컨트롤러(Fable 5, 이 세션)가 diff를 확인하고 커밋 컨트롤러는 매 태스크마다: 보고 검토 → (필요시) 진행 레저(`progress.md`)와 계획서 갱신 → 다음 브리프 추출 → 다음 에이전트 디스패치, 를 반복했다. 복잡도가 높은 태스크(모달 시스템, 커뮤니티 게시판, 웹소켓 레이어, 웹챗) 4곳에는 **별도의 스팟 리뷰 에이전트**를 추가로 붙여 구현자와 다른 시각으로 검증했다. --- ## 숫자로 보는 결과 - **계획 태스크**: 20/20 완료 - **커밋**: 34개 (기능 20 + 리뷰 수정 4 + 사용자 QA 후속 수정 10) - **테스트**: 164/164 green (vitest) - **런타임 의존성**: 7개 (VueUse/axios/ofetch 등 전부 배제, 직접 구현) - **번들 크기**: 클라이언트 JS+CSS 약 1MB, `.output` 전체 gzip 991KB - **SDD 산출물**: 브리프+리포트 40개 파일, 총 1,958줄 — 태스크별 근거 기록 ## 왜 이 방식이 통했나 (요약) 1. **코드를 먼저 이해시키고**(병렬 탐색), **계획을 문서로 못박은 뒤**(writing-plans), **신선한 컨텍스트로 하나씩 구현시키고**(subagent-driven-development), **리뷰로 걸러내고**(스팟 리뷰), **사람이 실사용으로 마무리**하는 — 각 단계가 서로 다른 종류의 실수를 잡아낸다. 자동화된 리뷰는 웹소켓 레이스나 CSS 회귀를 못 잡았지만, 실사용 QA가 잡았다. 반대로 KIMP 산술 정밀도나 CDN 검증처럼 사람이 놓치기 쉬운 것은 리뷰 단계가 잡았다. 2. **"구현자는 스테이징까지, 컨트롤러가 커밋"** 규칙 덕분에 모든 변경이 커밋 전에 한 번 더 사람(컨트롤러) 눈을 거쳤다. 3. 의존성 최소화라는 제약이 오히려 "이 라이브러리가 뭘 하는지 몰라도 됨"이 아니라 "우리가 뭘 만들고 있는지 정확히 알아야 함"을 강제해서, 버그의 원인 파악이 항상 우리 코드 안에서 끝났다(서드파티 블랙박스 없이).
2026-06-16
0
138
Grafana 웹페이지가 너무 느려서 메모리를 키웠다
모니터링 툴로 Prometheus + PLG 스택을 사용 중이다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/b53355ba-bd5e-41af-9d7f-61f4b0e88d56_image.png) Prometheus는 Go API에 Prometheus용 미들웨어를 넣어 메트릭 엔드포인트를 열어두고, 띄워둔 Prometheus 서버가 그걸 주기적으로 퍼가는 방식이다. Loki는 지정해둔 서버 로그 위치를 Promtail에 알려주고, Promtail이 Loki로 로그를 주기적으로 푸시해주는 방식이다. Grafana는 이런 로깅 툴들이 제공하는 데이터를 소스로 모아 GUI로 보기 쉽게 구현해준 웹사이트 스택이고, 보통 Promtail + Loki + Grafana를 묶어 PLG스택이라 일컫는다. `docker-compose`로 구동중인데, 각 툴의 메모리 상한을 다 더해서 600MB 정도로 너무 타이트하게 잡아놨더니 Grafana 웹페이지에 접속하면 아예 뭐 다운되듯이 버거워하길래 `docker-compose.yml` 세팅에서 아래처럼 좀 더 늘려놨다. ```yml Prometheus: 512MB Loki: 512MB Promtail: 128MB Grafana: 150MB ``` Grafana를 좀 더 주는게 좋을 것 같긴 한데 위 상태로도 지금은 매우 쾌적하게 잘 구동된다. 메모리는 너무 가난하게 쓰면 안 되는 것 같다.
비트코인 블로그
2026-05-26
0
220
Lightsail은 아주 좋은 결정이었다.
[이 글](https://blog.coinsect.io/posts/lightsail-data-transfer-out)에서 기존 EC2에 몰려있던 여러 서비스들 중, Data Transfer Out 트래픽을 많이 발생시키는 업비트 웹소켓 프록시 서버만 $12짜리 Lightsail 인스턴스로 옮겼다는 이야기를 했다. **그건 아주 좋은 결정이었다.** 며칠 경과를 지켜봤는데, 일일 6~7GB의 트래픽이 Lightsail로 이동한 것을 확인할 수 있었다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/b5ab4177-6d16-479f-a2d9-62bf2b4e62db_image.png) 이 추세라면 월간 200GB 이상의 트래픽이고, 웹소켓 트래픽이 커질 경우 수 TB 까지도 육박할 수 있는 상당한 양이다. 다행히, 계약한 lightsail 인스턴스는 3TB까지 월 $12로 처리 가능하므로 만약 EC2에서 이 트래픽을 보낼 경우 지불해야 할 $100 이상의 비용에 비하면 아주 저렴하다. 성공적으로 잘 옮겨졌다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/8bfa3840-7d3b-47bb-9cf4-6efce3bd63ac_image.png) CPU도 딱 웹소켓 프록시 서버 하나만 띄워놓아서 그런지 2~4% 수준으로 아주 소소하게 사용중이다. 반면, MySQL, Redis, Typesense, 수많은 nodejs 서버와 go 서버를 띄워놓은 EC2 쪽의 CPU 사용률은 40% 선에서 5~6% 선으로 크게 떨어졌다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/b3a3a9ee-1533-4148-9c3e-696d890afaa2_image.png) 아무래도 일정 선까지는 CPU 사용률이 급격히 올라가지 않다가, 조금 무리가 싶은 상황이 되면 확 뛰는게 아닌가 싶은 생각이 든다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/ce188904-ede8-4828-afd3-56f73928cbbe_image.png) CPU 크레딧도 Lightsail 사용 전 CPU 사용률이 높았던 시절에는 계속 0으로 유지되다가, CPU 사용률이 현저히 떨어지자 다시 차올라서 최대치인 576까지 계속 저축되는 것을 확인할 수 있었다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/d2baee2b-a6f3-4da0-b6de-6a6faecc90b7_image.png) 이번 달 요금인데, 이 중에서 다음 달부터는 없어지거나 줄어들 것으로 생각되는 것을 숫자로 표시해보았다. 1. CPU 사용률이 떨어지면서 Credit이 정상적으로 누적될 수 있는 구조가 되었기 때문에 크게 줄어들 것으로 기대한다. 2. Godaddy에서 샀던 gravex.app 도메인을 route53으로 이전하면서 지불한 일회성 비용. 3. 이게 문제의 데이터 전송비인데, 이것도 앞으론 아마 100GB 미만으로 맞출 수 있을거라서 거의 0에 가깝게 줄어들 것으로 기대해본다. 4. 이건 CloudFront가 최근들어 새 배포를 생성하면 `보안 보호 활성화`를 디폴트로 설정하도록 유도하기 때문에 별 생각 없이 그대로 Next Next Next 하다 보면 낚여서 지불하게 되는 비용인데, 그거 없어도 된다. 이걸 설정하면 ACL 규칙이 생성되는데, 이 규칙 하나 당 일정 비용($5 정도)을 트래픽이 없어도 무조건 과금한다. 그게 필요할 정도의 서비스면 이 $5가 아깝지 않을 서비스이니 써도 되겠지만, 나중에 필요할 때 켜도 되니 웬만하면 `보안 보호 비활성화`를 선택하자. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/5bef512a-04c1-414d-a77c-ea564a47b698_image.png) **끗**
비트코인 블로그
2026-05-20
0
263
업비트 웹소켓 프록시 비용 폭등 문제 해결하기: 웹소켓 프록시를 Data Transfer Out이 싼 인스턴스로 이전 (Lightsail)
[이 글](https://blog.coinsect.io/posts/upbit-websocket-go-proxy)을 쓸 때만 해도 웹소켓 Proxy서버를 만들어서 throttling을 적당히 걸어주면 **우아하게** 비용 문제를 해결할 수 있을 줄 알았다. 그러나 이런저런 삽질을 해본 결과, 무슨 최적화나 꼼수를 써도 결국 EC2 => 외부로 나가는 트래픽은 전부 Data Transfer Out을 늘리게 되며, EC2는 기본적으로 이 비용이 매우 비싸기 때문에 비용을 크게 절감할 수 없음을 깨닫게 됐다. 따라서, 최종적으로 선택한 방법은 Data Transfer Out 비용 자체가 싼 인스턴스, AWS 기준 Lightsail을 웹소켓 전용 서버로 운용해보자는 것이었다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/7fbda97d-49f0-44ce-8734-d648a58a9ff6_image.png) EC2는 기본적으로 Data Transfer Out에 대해 100GB까지는 무료로 제공하는데, 수 년간 운영해본 경험상 수많은 코인 티커에 대해 내 서버에 연결된 클라이언트들에 브로드캐스팅하다보면 저 한도는 아주 가볍게 넘었다. 따라서 저 트래픽 자체가 싼 곳을 선택하는 방법 외에는 뾰족한 비용 절감 방법이 없다. Oracle이나 Vultr 등 저 비용이 없거나 저렴한 Cloud들도 많았지만 그냥 AWS 내에서 해결하고 싶어서 찾아보니 Lightsail이 딱이었다. 예전부터 들어서 알고는 있었지만 굳이? 하다가 비용이 너무 많이 나오다 보니 [...] 오늘자로 하나 생성했다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/d2bbf509-b393-44f5-a0e3-850cecdf52e8_image.png) $12 플랜은 첫 90일은 무료(!)이고 거기에 무려 3TB Transfer까지 패키지로 포함되어 있다. 그래서 여기서 인스턴스를 만들고 방화벽을 열었다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/d283cf20-5347-4a83-bbaf-f73a84e25167_image.png) 그리고 Route53에서 `https://cheap.coinsect.io` A record를 추가해서 방금 만든 Lightsail 인스턴스를 바라보게 하고, Lightsail 인스턴스에서는 Nginx 프록시로 localhost의 go websocket proxy서버를 바라보도록 설정해뒀다. 그리고 클라이언트에선 웹소켓 연결은 `https://cheap.coinsect.io`로 하도록 만들고 배포했다. **기대되는 효과**: - 이제 스로틀링 간격을 아주 짧게 주면서 실컷 Data Transfer Out시켜도 됨 (물론 throttling을 아예 안 주는 것보단 주는게 나음) - 비용은 앞으로 세 달간은 $0, 그 뒤로는 $12 상한으로 줄일 수 있음. 기존에 트래픽 비용만 $100씩 나가던 걸 삭제. - 아울러 내 메인 인스턴스(EC2 medium)에서는 CPU도 이 웹소켓 서버가 빠진 만큼 적게 쓰기 때문에, CPU Credit이 누적되는 효과도 기대해본다. **단점**: - 관리 포인트가 늘어남... PuTTY에서도 이제 두 인스턴스를 들락날락거려야 하고 IP도 2개를 관리해야 함 ㅠ.ㅠ 그래도 매달 $50~$100이 뭐 하늘에서 떨어지는 건 아니니까
비트코인 블로그
2026-04-01
0
513
업비트 웹소켓 요청제한 우아하게 해결하기 - Go 프록시 서버
벌써 쓴지 1년이 넘은 (시간 진짜 빠르네...) [업비트 웹소켓 요청제한 우회하기](https://blog.coinsect.io/posts/bypass-upbit-websocket-rate-limit) 글의 후속작업을 오늘 다시 진행했다. [Cloudflare 터널로 돌려보기도](https://blog.coinsect.io/posts/upbit-websocket-aws-data-transfer-cost-reduction) 했지만 어차피 AWS Data Transfer는 Cloudflare로의 Outbound 트래픽으로 간주되어 과금은 줄어들지 않았었다. 결국 내린 결론은, 어쨌든 사용자들이 본인 기기에서 내 사이트를 통해 직접 업비트 웹소켓에 붙으면 나에게 있어 비용적으론 제일 좋지만 [코인충](https://coinsect.io) 도메인에 대해서 rate limit이 걸리기 때문에, 내가 업비트 웹소켓에 붙는 프록시 서버를 하나 만들고, 유저들이 여기를 보도록 하자는 것이었다. 작년에 시도한 첫째 방법과는 약간의 차이가 있는게, 그때는 특별히 프록시 서버 코드를 짠게 아니라 아래처럼 그냥 Nginx단에서 그대로 "우회"(Proxy)만 시켰었다. ![](https://d1085v6s0hknp1.cloudfront.net/chat/8b075a34-9e97-42b8-ba4f-69fa271e988b_GnB2wxCaQAAoxC7.jpg) 그런데 이번에는 업비트 웹소켓 서버에 연결될 프록시 서버를 Go로 짜고, Nginx가 이걸 바라보게 처리해뒀다. 물론 Nginx는 뭐 꼭 필요하진 않은데 기존에 적어둔 위 세팅을 Go 서버가 뜬 localhost로 바꾸는 등 조금만 건드리면 됐어서 이 세팅을 가져갔다. 굳이 Go로 짠 이유는? 이미 서버에서 systemctl을 돌리고 있어서 세팅이 편하기도 하고, 무엇보다 **Nodejs보다 훨씬 빠르니까**. [코드](https://github.com/kispi/coinsect_api/blob/main/proxy_upbit/main.go) 내용을 간략히 설명하자면, - **[비용을 줄이는 핵심]** 스로틀링. 업비트에서 데이터가 내 서버로 왔다고 바로 연결된 클라이언트들에 브로드캐스팅하는게 아니라, 일정 시간마다 보내서 트래픽을 대폭 줄임. - 업비트 서버와 **단 하나의 웹소켓 연결**만 유지. - 연결이 끊어지면 자동으로 재연결 시도. - 클라이언트들의 구독을 관리하고 각 클라이언트가 요청하는 티커들의 구독의 합집합을 구해서 업비트에 새로 요청. - 클라이언트에 브로드캐스팅할때도, 매 클라이언트가 구독한 n개의 티커 중 업데이트된 m개를 보내기 위해 서버가 m번의 `writeMessage`를 하는게 아니라, 업데이트된 m개를 합쳐서 한 번만 `writeMessage`를 함. - 업비트 웹소켓 데이터는 바이너리로 오기 때문에 이걸 JSON으로 파싱해야하는데, 이 로직을 클라이언트에서 서버로 옮김. (서버가 구조를 알아야 필요한 정보만 최신으로 갱신하며 최신 상태를 유지할 수 있으므로) 이처럼 위 Go 서버는 꽤나 복잡한 일들을 하는데, 이렇게 처리를 해도 어쨌든 비용은 늘어날 수 밖에 없다. 그렇지만 업비트 <=> n대의 클라이언트의 모든 트래픽을 무식하게 중개하던 먼저번의 방법처럼 거의 1TB에 달하는 outbound 요금이 부과되는 일은 없을거라고 기대해본다. 이 문제로 고통을 겪는 업비트 코인시세 중개 서비스 개발자분들이라면, 저 소스코드를 hostname 체크하는 부분 정도만 변경해서 거의 그냥 그대로 써도 될 것 같다.
비트코인 블로그
2026-01-13
0
640
내가 요새 AI를 쓰는 방법들
2026년 1월 현재, 나는 AI를 어떻게 쓰고 있는지 정리해봤다. 정말 AI enthusiasts분들처럼 딥하게 쓰는 건 아닌데, 그럼에도 불구하고 AI 이전으로는 못 돌아갈 것 같은 상황까지는 온 느낌이다. 1. **영어회화**를 위해서는 ChatGPT를 쓴다. 그냥 무료 plan이고, 자기 전에 잠깐이나 심심할 때, 또는 운전 중에 음성모드로 해두고 영어로 대화한다. 영어 native 친구가 많지 않다면 좋은 대체재이고, 상대가 발음 때문에 못 알아들을까봐 주눅들지 않을만큼 자신감을 키우는데 아주 좋다. 다만 음성모드에선 대화가 끊기는지를 ChatGPT가 계속 감지하고 있기 때문에, 내가 머뭇거리면 바로 대답이 나오기는 한다. 이게 웬만한 인간보다 더 알잘딱깔센이라 장점도 있지만 내 외국어 실력을 키우는데 있어선 단점이기도 하다. 2. **프로덕트 아이디어나 기술적인 대화**는 주로 [구글 AI STUDIO](https://aistudio.google.com)에서 Playground => Gemini 3.0 Pro으로 맞춰두고 한다. Build 기능도 웹에서 간단하게 딸깍 앱 생성해보기는 좋은데, 이보다는 후술할 [Antigravity](https://antigravity.google/)를 사용하는게 개인적으로는 더 낫다. 거의 대부분의 **생산성을 위한 진지한 대화**는 Gemini 3.0 Pro와 하고 있다. 3. **개발**은 [Antigravity](https://antigravity.google/)를 사용중이다. Cursor나 Windsurf같은 AI IDE이고 그냥 구글 계정만 연동하면 무료 플랜으로도 사용 가능한데, 현 시점에선 바이브코딩이라는게 진짜 뭐 밈이나 애들 장난이 아니고 아예 **개발 메타 자체가 바이브코딩으로 바뀌었다**는 걸 실감할 수 있다. 실제 최근 몇 주 간은 내가 직접 쓴 코드가 거의 없다 [...] 최근 생전 한 번도 써보지도 않았고 지금도 잘 모르는 SvelteKit으로 [캘린더 & 노트 일정관리 앱](https://scheduly.gravex.app)을 하나 만들고 있는데, 내가 쓴 코드가 거의 전혀 없다. Antigravity 관련 글은 따로 쓸 예정인데, 그냥 신세계이다. 과장을 전혀 보태지 않고, 앞으로 내가 직접 코딩을 할 일은 거의 없지 않을까 생각이 들 정도이다. 이걸 사용해보면 전세계적인 대규모 사무직(특히 개발자) 해고 & 고용난이 자연스럽게 이해된다. 무료로도 Gemini 3.0 Pro, Flash, Claude Sonnet 4.5 등의 모델들을 일정량 사용할 수 있고 gemini 3.0 flash는 꽤나 quota가 널널한 편이긴 하다. 다만 직업 프로그래머라면 당연히 택도 없이 모자르니 유료를 쓰는게 좋다. 4. 심심할 때는 **Grok AI**를 써서 이미지를 생성하거나 동영상을 생성할 때가 있는데, 신창섭 이미지 한 장이면 '돈 좀 써라 쌀숭이들아!' 말하는 영상을 간단히 생성 가능하다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/1bcc4745-d03c-4146-b2f2-52f37f8cc47f_image.png) 다만 한글은 잘 안 되는 느낌이고, 무료는 quota가 제한적이다. 그냥 몇 번 테스트만 해볼 수 있는 수준이다. 그래도 일일 10회 정도는 되는 듯? 불과 1년 전이랑만 비교해봐도, 이미지나 동영상 생성시 input으로 주어진 이미지에 있던 **일관성을 유지하는 기술**이 너무나 좋아졌다. 5. **이미지 생성/수정**은 당연히 나노바나나다. 역시 Google AI Studio에서 Playground => Images를 선택 후 Nano Banana를 선택하고 프롬프트를 입력하면 된다. 이 무료 모델도 꽤나 좋지만, Pro는 또 차원이 다르게 성능이 좋다. 별의별 것들이 다 되는데, 인물 사진 한 장을 넣고 '증명사진을 만들어줘' '피규어를 만들어줘' '천장에서 찍은 각도로 바꿔줘' '트럼프랑 함께 서있는 모습을 그려줘' 등등을 요청하면 그린랜턴의 반지마냥 잘 만들어준다. 불과 1, 2년 전만 해도 로컬에서 stablediffusion webui 등으로 모델 넣고, LoRa 넣고 해서 이런저런 시도들을 열심히 하고도 퀄리티 좋은 그림을 뽑기 어려웠는데 그 시절과 비교하면 그냥 적당한 프롬프트만 입력해도 기깔나게 의도를 이해해서 text2img를 해주는 시대가 된게 참 놀랍다. 텍스트에는 너무나 많은 맥락이 빠져있는데 모델이 어떻게 사용자의 의도를 이처럼 센스있게 인지하는지 놀라울 지경이다. 써놓고 보니 ChatGPT는 이제 거의 쓰지 않고 Google쪽 프로덕트들을 많이 쓰는 느낌인데, 이 글을 쓰는 2026년 1월 시점에선 OpenAI의 미래에 다소 의문이 드는 상황이다.
비트코인 블로그
2025-12-01
0
423
리액트보다 다른 게 더 좋다 🫠
얼마 전, 이제는 리액트를 놓아주고 시그널 기반의 최신 프레임워크(ex: Vue, Svelte...)들을 쓰자는 취지의 [위시켓 글](https://yozm.wishket.com/magazine/detail/3153)을 보고 굉장한 공감을 했다. 이전에 [글로 쓰기도 했지만](https://blog.coinsect.io/posts/why-i-prefer-vue-over-react), 나 역시 리액트를 안 좋아한다. 2025년 지금까지도 리액트를 쓰는 건 과장 좀 보태서 마치 고수준 언어들을 써도 되는 상황에서 어셈블리를 쓰는 느낌이다. 리액트 컴파일러가 나왔다곤 하지만 여전히 개발자들은 `useCallback` `useMemo` 등 리렌더링 최적화에 신경을 써야한다. 아니 대체 왜? GC 없는 unmanaged 언어들은 잘 쓰면 성능상의 이점이라도 있지, 브라우저 렌더링 최적화를 **사람이** 해야하는 상황은 순수 짐이며 휴먼에러만 늘릴 뿐이라고 본다. 게다가 대세가 클래스 컴포넌트에서 함수형 컴포넌트로 넘어오며 `useEffect`를 라이프사이클 처리에 대한 일종의 핵(?)으로 쓰는 것도 굉장히 부자연스럽고 짜친다고 느껴지지 않을 수 없다. 뭐 애초에 함수형인데 Effect를 가질 수 있다는 것 자체부터 엄격하지 않았던 설계를 보여준다는 생각도 들고. 한 함수 내에서 두 개 이상의 `useEffect`가 서로 무한재귀를 일으키는 코드를 짜 본 실수는 누구나 해봤을 것이다. 난 이게 마냥 개발자의 역량 부족 때문만이라고 생각하지 않는다. 이젠 세상이 리액트를 놓아줄 때가 됐다고 생각하지만 그럴 기미가 보이지 않는다. 나 역시 여전히 누군가가 "딱 하나만 배운다면 뭘 해야 하는가?" 묻는다면 현실적 이유로 리액트를 이야기할 수 밖에 없다. 리액트의 아성은 언제쯤 꺾일까?
2025-11-13
0
363
Vue 프로젝트는 그냥 Nuxt를 쓰세요
최근에 [Nuxt](https://nuxt.com/)를 쓸 일이 있어 Nuxt 4 기반 프로젝트를 새로 만들어보았다. ``` npm create nuxt@latest <project-name> ``` 원래도 DX가 뛰어난 Vue를 오랜기간 써 왔기 때문에 학습곡선은 거의 0에 가까웠고, Remix 경험이 있어서 디렉토리 기반 라우팅에도 바로 적응했다. 레일즈마냥 워낙 많은 마법이 존재해서 "이게 왜 되지?" 싶은 것들이 좀 있는데, 몇 가지는 다음과 같다. - `~/app/components` 폴더에 파일을 생성하면 알아서 전역 컴포넌트로 등록되며, 다른 컴포넌트들에서 별도의 import 없이 사용 가능함. - `~/app/components/course/List.vue` 형태처럼 중간에 디렉토리를 낀 경우, 그것이 자동으로 prefix들이 되어 `<CourseList />`형태로 명명됨. - `.vue` 파일들 내부에선 `ref`, `computed`, `watch`, `onMounted` 등을 쓰기 위해 `import ... from 'vue'`를 할 필요가 없음. 그냥 글로벌하게 지원됨. - `~/app/composables/useMyHook.vue` 등으로 composables 폴더 안에 넣은 훅들 역시, `.vue` 파일들에서 별도의 명시적 import 없이 자동으로 접근 가능함. - `~/app` 폴더 말고, `~/server/api/서버파일들`을 작성하면 기본적으로 동일한 포트(default: 3000)의 `[METHOD] /api/...` 백엔드 호출들을 처리해줌. 이를테면 `~/server/api/me.get.ts`는 `GET /api/me`의 핸들러임. 근데 나는 서버의 경우는 아래처럼 라우트와 핸들러를 직접 정의하는 방식을 선호해서 개인적으로는 불호이고, 프로덕션에서는 다른 포트에 별도 서버를 띄워서 쓸 듯. ``` app.get('api/me', userController.me) ``` 그러나 좀 규모가 있는 서버가 아니라 몇 개의 endpoint로 해결 가능한 간단한 프로젝트라면 프론트엔드와 백엔드에서 일부 코드(ex: 타입, 간단한 헬퍼 등)는 공유할 수도 있어서 편리할 듯 하기는 함. - 내장 함수 중에 `$fetch`가 있는데, 흔히 익숙한 axios 등의 HTTP 클라이언트와 사용법도 거의 동일하니 그냥 이걸 쓰면 된다. 또 `$fetch`를 래핑한 경량(?) tanstack-query 느낌의 `useFetch` 훅도 존재한다. - `useAsyncData`훅이 있는데, `<script setup>` 내에서 바로 `await useAsyncData(...)` 형태로 사용하면 매우 간단하게 비동기 데이터의 SSR 처리가 가능하다. 브라우저에서 소스보기를 하면 비동기 데이터들이 마크업에 잘 들어가 있는 것을 확인할 수 있다. 원래 이런 SSR을 직접 하려면 프레임워크가 제공하는 `renderToString` 등의 함수를 직접 SSR 서버에서 실행해줘야 하는데, 그런 복잡한 기믹을 이해할 필요가 없으므로 편리하다. ([물론 공부해보는 것은 좋다.](https://blog.coinsect.io/posts/seo-ssr-spa-universal-rendering)) ## 결론 새로운 Vue 프로젝트를 진행하고 싶다면 별 고민 없이 그냥 Nuxt로 하면 된다. SSR이나 SEO를 염두해둔 경우라면 당연하고, 그냥 생 SPA로 서비스하는 경우라도 그렇다. Nuxt는 Vue보다 더 opinionated 되어 있고, 커뮤니티의 오랜 고민으로 프로젝트 구조가 좀 더 de facto에 가까워진 형태라 할 수 있으니 그냥 망설임 없이 따라가도 괜찮다고 하겠다. 뿐만 아니라 이젠 느려터진 Webpack은 좀 그만 봤으면 싶은 상황에서 기본 번들러도 Vite이니 더할 나위 없이 좋다. 필자가 선호하는 Vue의 컴포넌트 선언 방식은 [이 글](https://blog.coinsect.io/posts/vue-de-facto-script-setup-typescript)에 적어두었다.
2025-07-08
0
3871
토스페이먼츠 프론트엔드 직군 직무면접 후기
원티드로 딸깍 지원하고 별 생각을 안 하고 있었는데 서류 합격이 되어서 과제 전형을 수행하라는 메일을 받았다. NDS 서약 때문에 자세히는 얘기할 수 없지만, 일정 시간을 주고 실제 입사한다면 구현해볼 법한 기능을 구현하는 과제였다. 개인적으론 구현해야 할 양이 좀 많다고 생각했으며, 꽤나 체력적으로 부쳤지만(~~늙은듯~~) 어떻게 꾸역꾸역 해서 제출했다. 아마 다 하지 못하는 지원자도 상당히 많겠다는 생각이 들었다. 기능은 완벽하게 다 구현을 했고, 모던 프론트엔드 앱의 전형적인 스펙을 충족해서 제출은 했지만 시간 관계상 충분히 리팩토링을 하지 못해 마음 한 켠에 찝찝함이 있었다. 그런데 다행히 과제전형도 통과가 됐다. 그래서 오늘 화상으로 약 1시간 좀 넘게 면접을 봤는데, 내 화면을 면접관분들께 공유드리면서 코드를 같이 살펴보며 진행되었다. "이 지원자를 떨어뜨리고 말겠다" 이런 느낌으로 후벼파는 질문이라기보다는, 이걸 진짜 남이 해준게 아닌 본인이 이해하고 코딩한게 맞는지를 검증하는 정도라는 느낌이 들었다. 날 담당해주신 분들의 성향이 부드러우셔서 그런 것인지 시종일관 미소로 대해주셔서 크게 긴장하지 않고 잘 볼 수 있었고, 코드 리뷰에 이어서 내 이력서를 살펴보았다. 내가 내 개인 프로젝트([코인충](https://coinsect.io) 등)에 치중하느라 생각보다 전 직장에서 했던 일을 부실하게 기술했다는 것을 알게 되었다. 그래서 전 회사 사이트에 접속해서 화면 공유로 내가 만든 기능들을 설명드리며 면접을 진행했다. 물론 이력서도 했던 일들을 자세히 나열해서 보완해야겠다고 생각했다. 면접에서는 내가 한 일을 전부 다 얘기할 시간이 없기 때문에, 이력서에 내가 수행한 일들을 미리 상세히 나열해서 면접관들이 "이 사람은 어떤 일들을 할 수 있겠구나" 미리 알 수 있도록 하는 것이 대단히 중요하다는 점을 새삼 생각해보게 됐다. 끝으로 내가 소프트스킬이 좋은 편이라는 점을 면접관분들께 어필하며 좋은 분위기 속에 마무리가 되었다. 면접은 언제나 좋은 분위기 속에 끝나기는 하더라... 맨날 - [회사명] 지원해주셔서 감사합니다 - ~님, 지원 결과를 안내드립니다 이런 메일을 보게 되어서 문제지 🤣 최종합격이 되든 안되든 여기까지 온 것만 해도 아직 시장에서 수요가 있구나 싶어서 자존감이 살짝은 올라간다. 뭐 100 군데 정도 지원하면 2, 3곳은 붙겠지. --------- 2025-07-10 (목) 내가 면접관이었어도 떨어뜨리겠다 싶을 정도로 아쉬움이 남는 면접이었는데 역시나 떨어졌다. 어찌보면 좀 React에 관한 기초적인 질문들이 있었는데도 거기에 대해 제대로 대답하지 못했었는데, 사실 연차로 봤을 때 술술 막힘없이 대답해야 하는 부분들이어서 반성하게 되었다. 어쨌든 토스 커뮤니티에서는 어떤 점들을 중요하게 여기고 점검하는지 경험이 쌓였으니 그것만 해도 만족한다.
2025-05-28
0
1113
업비트 웹소켓 프록시 비용 폭등 문제 해결하기: Cloudflare 터널 활용기
바로 직전 글이 [업비트 웹소켓 API의 Origin별 Rate Limit 우회하기](https://blog.coinsect.io/posts/bypass-upbit-websocket-rate-limit)였다. 결론적으로 거기까진 성공적이었으나, 모든 트래픽이 죄다 내 ec2 인스턴스로 들어오는 터에 비용이 갑자기 엄청나게 증가했다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/7fbda97d-49f0-44ce-8734-d648a58a9ff6_image.png) 돈도 안 되는 서비스를 운영하면서 저런 비용을 쓰기는 싫기 때문에 방법이 없을까 ChatGPT에 물어보았더니([대화](https://chatgpt.com/share/6836f3f3-2ba0-8006-a02d-6e8bc43d9245)), 결론적으로 Cloudflare를 쓰라는 권유를 받아서 [cloudflare](https://www.cloudflare.com)에 계정을 만들고 내 서버에서 필요한 절차에 따라 설정을 해주었다. 크게 보면 1. nginx에서 Cloudflared용 내부 포트 추가 2. cloudflare tunnel cli 설치 / 인증 / 터널 생성 / 구성파일 생성 3. 데몬(systemctl)으로 등록 요 과정이었고 매우 간단하게 끝났다. Cloudflare Tunnel을 쓰면 내 서버와 Cloudflare 네트워크 사이의 터널을 통해서 "내부 트래픽"으로 처리되기 때문에, Data Transfer Out의 비용이 줄어들 수 있다고 한다고는 하는데, 실제 효과가 어떨지는 한 달을 기다려봐야 알 수 있겠다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/55d887ae-b051-4b59-aa02-07dc7b5da9b3_image.png) 그리고 이제부터는 Cloudflare를 통해 DNS Records를 처리하기 때문에, 기존에 사용중이던 AWS Route53에 등록된 DNS Records는 대부분 삭제하고 Cloudflare쪽으로 이전했다. 이게 CDN Edge가 Cloudflare의 한국 리전이 아닌건지 뭔가 체감상 Proxied면 더 느린 것 같아서 대부분 DNS only로 돌려놓고, 이 난리를 친 근원인 api 도메인만 Proxied로 설정해두었다. 아마 가장 좋은 해결책은 웹소켓용 서브도메인을 따로 파서 그것만 Proxied를 걸어두고 api 도메인도 그냥 DNS only로 쓰는게 맞는 것 같기는 하다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/74f2067d-08b9-433b-9e0f-ee34b584ff4a_image.png) --- 2025-06-18 추가 위와 같은 조처들에도 불가하고 **비용이 눈에 띄게 줄어들지는 않는 것으로 확인**되어, 다시 cloudflare를 제거하고 aws route53으로 변경... ㅠ.ㅠ ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/924f12f0-d87c-49fd-8018-199ee73ba35d_image.png)
비트코인 블로그
2025-03-27
0
7068
업비트 웹소켓 요청제한(초당 5회) 우회하기 (nginx 프록시)
필자가 운영중인 암호화폐 시세 정보 제공 사이트 [코인충](https://coinsect.io)에서는 업비트 웹소켓 API에 자주 붙는다. 일례로 메인 화면에는 아래와 같은 영역이 있고, ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/ffdc4b81-9546-4359-a200-d1c0280e63c8_image.png) 김프가와 비슷하게 아래와 같은 페이지도 존재한다. ([링크](https://coinsect.io/prices)) ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/6920d7c5-5f73-447a-9a62-29857b89d8f7_image.png) 근데 어느 순간부터 업비트와의 웹소켓 연결이 잘 안 되기에 문의를 했더니, 2024-07-17부터 웹소켓 요청 헤더에 Origin이 있을 시 rate limit이 적용된다는 답변을 받고 해당 문서를 안내받았다. ([링크](https://docs.upbit.com/kr/changelog/origin_rate_limit)) ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/eec6fc97-6ab2-4ff7-82da-50b83d89649e_image.png) 위 사진처럼 초당 5회까지만으로 요청이 제한되는 정책이 추가되었는데, 이는 동시접속자수가 수십~수백명에 육박하는 웹사이트를 운영하는 상황에선 턱없이 부족한 수치이다. 일반적으로 브라우저에서 웹소켓 요청을 하면, 브라우저가 자동으로 요청 헤더에 'Origin'을 현재 도메인으로 넣어서 요청한다. 따라서 이 부분은 어떤 꼼수를 쓰든 클라이언트에서 해결할 방법은 없다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/288fb894-e941-4a26-837d-960c8e1444d2_image.png) 따라서 웹소켓 요청 헤더에 Origin을 보내지 않는 방법이 없을까 고민하다가, 어차피 사용중인 nginx 서버에 업비트 웹소켓 우회용 프록시를 하나 뚫기로 했다. ![](https://d1085v6s0hknp1.cloudfront.net/chat/8b075a34-9e97-42b8-ba4f-69fa271e988b_GnB2wxCaQAAoxC7.jpg) 클라이언트에서도 웹소켓 요청을 내가 운영중인 프록시 서버로 하도록 변경하고 문제는 해결되었다. 다만 원래 유저들의 클라이언트 <-> 업비트가 바로 연결되었던 것과 달리, 이제 내 서버가 프록시로 사용되기 때문에 컴퓨팅 리소스가 더 소모되는 문제는 있다. 트래픽 추이를 지켜봐야 하겠다.
비트코인 블로그
2024-10-24
0
743
Vue 컴포넌트는 어떻게 작성하는게 현시점 de facto일까?
## Options API Vue 2 에서는 아래와 같이 작성하는 Options API가 주류였다. ```javascript <script> export default { props: { // 부모로부터 넘겨받는 값들 }, methods: { // 메소드들 }, computed: { // 계산된 값들 }, data: () => { // 데이터 }, mounted() { }, } </script> ``` 여기에는 다음과 같은 단점들이 있었다. - 컴포넌트 내에 특정 비즈니스 로직을 구현하는 경우, 이 로직들이 각각 `methods`, `computed`, `data` 등 각 속성별로 분리되어, 덩어리째 재사용이 어려움. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/ea321963-4a64-45f1-80aa-84183bf26c73_image.png) 이 그림은 Options API의 단점을 명확하게 보여준다. - `this`의 사용. 나는 코딩을 할 때 this나 self 등의 개념을 사용하지 않는 것이 좋다고 생각한다. Python, Java, Ruby 등 이것이 혼란을 줄 여지가 없는 언어들의 경우는 모르겠으나, JS의 경우는 그것이 호출되는 컨텍스트에 따라 가리키는 대상이 바뀔 수 있기 때문에 (e.g. [화살표 함수는 컨텍스트를 만들지 않음](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/Arrow_functions)) 객체 내에서 자기 자신을 인용할지라도 인스턴스의 이름을 항상 적는 것이 좋다고 생각한다. 이것은 추후 코드를 리팩토링하며 코드를 이리저리 옮기는 과정에서 this가 무엇을 가리키는지 신경 쓸 필요가 없도록 만들어준다. 또, computed같은 경우 정의할 때는 함수로 정의하나 호출할 때는 그냥 `this.someVal`과 같이 '값'을 호출하는 방식으로 사용하게 되니, 사실 초심자 입장에서는 혼란스럽다. Options API는 Composition API가 등장하기 전의 과도기적 형태였다고 생각한다. ## Composition API Vue 3부터는 Composition API가 공식적으로 권장사항이 되었다. ```javascript <script> import { ref, computed, onMounted } from 'vue' export default { setup() { const myData = ref() const myComputedVal = computed(() => { ... }) const init = () => {} onMounted(init) }, } ``` Composition API는 이처럼 'vue' 패키지에서 반응형 변수를 선언할 수 있도록 해주는 `ref`, `computed`나 `onMounted`와 같은 lifecycle hook을 import하여 `setup()` 안에 구현하는 방식이다. 이것의 가장 큰 장점은 `ref`, `computed`, `onMounted`, `watch` 등의 기능들이 `vue` 패키지를 통해 제공되기 때문에, `.vue` 파일 뿐 아니라 일반적인 `.js` 파일들에서도 사용할 수 있게 되었다는 것이다! 이처럼 재사용 가능한 로직들의 구현을 `.vue` 파일 외부로 빼낼 수 있게 되었기 때문에, `.vue` 파일들은 좀 더 `<template>`과 `<style>` 등 눈에 보이는 부분에 집중하고, 로직들은 외부로 묶는 형태의 프로젝트 구성이 용이해졌다. 누가봐도 리액트의 훅에서 영감을 받은 것이 명확해보이는 이 스펙은 Vue에서는 [composable](https://v3-docs.vuejs-korea.org/guide/reusability/composables.html)이라 불린다. ```javascript // my-hook.js import { computed } from 'vue' export const useMyHook = () => { const todayISO8601 = computed(() => (new Date()).toISOString().split('T')[0] ) return { todayISO8601, } } // MyComponent.vue import useMyHook from '@/hooks/my-hook' <template> <div>{{ todayISO8601 }}</div> </template> <script> export default { setup() { const { todayISO8601 } = useMyHook() return { todayISO8601, } }, } </script> ``` 상태관리나 생명주기 등과 무관한 단순 기능에 해당하는 함수들은 보통 helper라고 구분하면 되겠고, `ref`, `computed` 또는 lifecycle 함수나 상태 등이 엮이는 함수들은 composable(= hook)로 구분할 수 있다. ## [`<script setup>`](https://vuejs.org/api/sfc-script-setup) 그런데 `<script>` 내부에서 매번 `export default { setup() {} }`을 작성하고, 템플릿에 노출할 값들을 명시적으로 `return`하는 것도 사실 귀찮은 보일러플레이트이다. 그래서 그것에 대한 설탕으로, `<script setup>`이라는 것이 존재한다. ```javascript <template> <div>{{ todayISO8601 }}</div> </template> <script setup> import useMyHook from '@/hooks/my-hook' const props = defineProps(...) const emit = defineEmits([...]) const { todayISO8601 } = useMyHook() </script> ``` 위 코드에서 알 수 있듯이, 번거로운 보일러플레이트가 꽤나 간소화된다. 또한 선언한 변수나 함수들은 별도의 `return` 없이 자동으로 템플릿에 노출되어 사용할 수 있게 된다. 그리고 prop, emit은 defineProps, defineEmits를 통해 정의하는데, 이런 함수들은 딱히 명시적으로 import하지 않아도 된다. ## Typescript 처음 프로젝트를 생성할 때부터 Typescript 기반으로 생성했다면 바로 사용할 수 있고, 기존 프로젝트에서 추가하고 싶은 경우라면 터미널에서 간단히 ``` vue add typescript ``` 를 하면 된다. (`vue-cli`가 글로벌로 설치되어 있는 경우 가정) 타입스크립트를 설치했다면 이후부터는 `<script lang="ts">`과 같이 `lang="ts"`만 붙이면 된다. 물론 당연하게도 `<script setup lang="ts">`도 가능하다. ## 내가 새로 Vue 프로젝트를 만든다면? - Vite - Typescript - script setup - Vuex (X) [Pinia (O)](https://pinia.vuejs.kr/) 요렇게 scaffolding 할 것 같다. 현 회사에서의 3년 넘은 Vue 3 프로젝트 역시 최근에 script setup + typescript로의 전환을 마쳤다. 너무나 작업량이 많아 엄두를 내지 못 하고 있었는데 github copilot의 도움으로 비교적 빠르게 마칠 수 있었다.
비트코인 블로그
2024-10-11
0
3166
내가 golang을 사랑하는 이유
커리어 초창기에 일했던 회사에서 나는 golang으로 백엔드에 입문했다. 물론 그 이전에도 PHP + Codeigniter 조합으로~~틀~~ SSR을 했던 경험이 있긴 한데, 본격적으로 백엔드 + 프론트엔드가 분리된 웹앱을 만들어본 경험에서 첫 도구는 golang이었다. 그리고 이 때 받았던 인상은 너무나 긍정적이었어서, 지금도 최애인 nodejs와 병용하고 있다. ## 마스코트 ![](https://go.dev/blog/gopher/header.jpg) ## 누가 쓰나? ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/16c6bdd9-b509-4f79-913d-06d377396a78_image.png) 이렇게 이름만 대도 알 만한 굴지의 기업들~~이띄리움~~이 golang을 사용하고 있다. ## 웹 프레임워크들 - `net/http` => golang 자체 패키지. - [gin](https://github.com/gin-gonic/gin) - [beego](https://github.com/beego/beego): 서두의 회사를 다닐 때는 사장님이 ORM만 여기서 가져와 조립해 제작하신 자체 프레임워크를 사용했었음. - [echo](https://github.com/labstack/echo) ## 장점 - **쉽다.** 일단 예약어부터가 불과 아래의 25개에 불과하다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/0acf67e3-085e-41ec-95ee-054c07ce696c_image.png) 앞서 말한 회사에서의 경험을 돌이켜보면, 이미 잘 짜여진 코드베이스를 보며 학습하면서 내가 처음으로 API를 추가해보기까지 고작 일주일이 걸리지 않았던 것으로 기억한다. 당시 나의 전반적인 SW 개발 경험이 매우 적었던 것을 고려하면 매우 놀라운 학습곡선을 지닌 언어라는 생각이 든다. - **강력한 타입 추론/체크** => 런타임에서 nil pointer를 참조하는 에러를 제외하면, 대부분의 실수가 컴파일러를 통해 미리 방지된다. - 인터프리터처럼 쓸 수 있을 만큼 **컴파일 속도가 매우 빠르다.** 소스코드를 고치고 `go build && [컴파일된 바이너리명]`를 실행하면 불과 몇 초 만에 변경사항이 반영된 바이너리가 실행된다. 나는 개인적으로 네이티브 앱 개발을 싫어한다. 컴파일이 오래걸리기 때문이다. 반대급부로 hot-reloading이 지원되는 모던 웹개발을 좋아하는데, 성격이 급한 사람으로서 golang은 너무 만족스러운 도구이다. 어찌 그리 컴파일이 빠른지 ChatGPT에게 물어봤더니 뭐 이렇다고 한다. ``` 간단한 의존성 관리: Go는 복잡한 헤더 파일을 사용하지 않으며, 대신에 패키지 단위로 의존성을 관리합니다. 이는 C/C++처럼 매번 복잡한 의존 관계를 컴파일러가 추적하지 않게 만들고, 필요한 부분만을 컴파일하는 데 도움을 줍니다. 병렬 컴파일: Go는 멀티코어 시스템에서 병렬로 코드를 컴파일할 수 있도록 설계되었습니다. 이를 통해 여러 파일이나 패키지를 동시에 컴파일하여 속도를 극대화할 수 있습니다. 간결한 언어 설계: Go 언어 자체가 복잡한 기능(예: 매크로, 템플릿, 상속 등)을 최소화하여 컴파일러가 더 빠르게 코드를 처리할 수 있도록 설계되었습니다. 고정된 타입 시스템: Go는 타입 추론을 지원하지만, 정적 타입 시스템으로 고정된 타입을 사용하여 컴파일러가 코드를 분석하는 데 드는 시간을 줄입니다. 이것은 특히 대규모 코드베이스에서 큰 이점을 제공합니다. 링크 시간이 짧음: Go는 자체적으로 링크러(linker)를 최적화하여 바이너리를 생성할 때 빠르게 링크 작업을 수행할 수 있습니다. 또한 Go는 자체 런타임을 포함하는 특성 덕분에 다른 외부 라이브러리와의 링크 작업이 복잡하지 않습니다. 단일 바이너리 생성: Go는 모든 종속성 파일을 하나의 정적 바이너리에 포함하는 방식으로 컴파일되기 때문에 실행 파일을 만드는 과정에서 추가적인 동적 링크 작업이 필요하지 않습니다. 이 역시 속도에 기여하는 요소입니다. ``` - **빌드된 파일이 바이너리 하나로 나온다.** 개인적으로 이게 너무나 큰 장점이라고 생각하는데, 파일을 실행할 타겟 머신에서 컴파일만 성공한다면 그걸로 끝이다. 그냥 실행하면 된다. 일반적인 스크립트 언어들처럼 서버에 코드를 노출할 필요도 없고, `systemctl`등 일반적으로 사용되는 리눅스 데몬들과의 궁합도 매우 좋다. 뭐 프로덕션에서 그럴 일은 없겠지만 바이너리이므로 당연히 `nohup`과 사용할 수도 있다. - **강력한 성능.** go는 원래 동시성 처리를 염두에 두고 goroutine이라는 경량 스레드를 사용하도록 설계되었다. 그리고 내장된 http 패키지는 request마다 이 goroutine을 할당해 처리하기 때문에, request마다 프로세스나 스레드를 할당하거나 이벤트 루프로 처리하는 방식에 비해 일반적으로 높은 rps를 보인다. 벤치마크들을 찾아보면 스트레스 테스트에서 대체로 golang 서버의 퍼포먼스가 타 언어 기반 서버들에 비해 상당히 높은 편임을 알 수 있다. - **강력한 비동기 지원.** 자바스크립트를 제외하면 대부분의 언어들은 동기가 기본이다. 윗줄이 완료되어야 다음줄이 실행되며, 이것은 논리적으로 자연스러운 사고의 흐름이기도 하다. golang도 기본적으로는 동일하긴 한데, 함수 실행시 단지 앞에 `go`를 붙이는 것만으로도 javascript에서 Promise 타입의 함수들을 실행하듯이 결과를 기다리지 않고 다음줄로 넘어갈 수 있다. ```go go doNotWaitForThisFunction() nextFunction() ``` 이 코드의 경우, `doNotWaitForThisFunction()`이 종료되기를 기다리지 않고 바로 다음 줄의 `nextFunction()`으로 넘어가는데, 다른 언어들에서는 일반적으로 이런 비동기 실행 구현이 간단하지는 않다. 워커나 스레드풀을 만들어서 함수를 거기 넘기고... golang에서는 이런 복잡한 고민들을 할 필요 자체가 없다. 알아서 적절하게 goroutine들을 할당하고 GC해준다. - **포인터가 존재한다.** 물론 C에서의 그것처럼 복잡하고 정교하게 컨트롤 할 수 있는 수준까지는 아니지만, 현재 내가 함수에 인자로 넘기는 것이 레퍼런스(받는 쪽에서 수정 가능)인지 값(복사되는 값이므로 받는 쪽에서 수정 불가능)인지가 코드에 보다 명확하게 드러나는 장점이 있다. 호불호가 있을 수 있기는 한데, 개인적으로는 좋았다. ```go package main import "fmt" func modifyValue(x int) { x = 10 // 복사된 값을 수정 } func modifyPointer(x *int) { *x = 10 // 원본 값을 수정 } func main() { a := 5 modifyValue(a) // 값에 의한 전달 fmt.Println(a) // 출력: 5 (원본은 그대로) modifyPointer(&a) // 포인터에 의한 전달 fmt.Println(a) // 출력: 10 (원본이 수정됨) } ``` ## 기타 - go 1.18부터 드디어 generic이 추가되었다! 난 generic이 없던 시절부터 go를 사용했기에 `int` `int64` 등 각 자료형별로 똑같은 로직을 복붙하며 괴로워했던 기억이 있다. ```go // no one wants to do this: func add(a int, b int) int { return a + b } func addInt64(a int64, b int64) int64 { return a + b } func addFloat64(a float64, b float64) float64 { return a + b } // generic was introduced in go 1.18 🥹 func add[T int | float32 | float64 | int64](a T, b T) T { return a + b } ``` - 같은 폴더 안의 파일들은 같은 패키지로 묶이며, 변수이든 함수이든 **첫글자가 대문자**이면 자동으로 export된다. 익숙해지면 편리하긴 한데, 모르는 사람 입장에서는 처음에 다소 황당하게 느껴질 수 있다. 이것은 struct를 선언할 때도 마찬가지이다. ```go func nonExportedFoo() {} func ExportedFoo() {} var nonExportedVar = true var ExportedVar = true /* 이 경우 Name 필드는 외부에서 접근 가능하나, age는 무시된다. 따라서 json.Marshal / json.Unmarshal시에도 age필드는 대상에서 제외된다. */ type Person struct { Name string `json:"name"` // 대문자로 시작: 인식 age string `json:"age"` // 소문자로 시작: 무시 } ``` - **매우 호불호가 갈리는 에러처리 방식**을 갖고 있다. 대부분의 언어들처럼 `try <=> catch` 구문으로 에러가 발생할 장소를 적당히 쫙 감싸서 처리하는 것이 아니라, 명확히 에러처리가 필요한 부분들에 하나하나 다음과 같이 명시적으로 `if`문을 넣는다. ```go result, err := fooThatCouldCauseError() if err != nil { return nil, err } return result, nil ``` 개인적으로는 조금 귀찮기는 하더라도 이렇게 하는 것이 더 견고하고 런타임 에러를 줄일 수 있는 방식이라 생각한다. 또 함수를 잘 설계하고 early return을 최대한 활용하며 `else`의 사용을 줄이는 mental framework에 어울리는 방식이라고도 본다. ![](https://www.dolthub.com/blog/static/46fd9e1eaaed072e99d32a35981525f1/65654/one-weird-trick.png) - 아무래도 강타입 컴파일 언어이다 보니 JSON을 다루는 것이 아주 편리하지는 않다. `map[string]interface{}`를 갖고 약간의 꼼수(?)를 쓰면 JavaScript에서 객체 리터럴(`{}`)을 사용하듯 임의 구조의 JSON을 좀 편리하게 다룰 수 있다. ```go type H = map[string]interface{} myObj := H{ "company": "coinsect.io", "devs": []H{ { "name": "Chris", "age": 18, }, { "name": "Bob", "age": 24, "salary": 200000, }, }, } ``` - 연속된 자료형을 표현하는 개념으로 'array'와 'slice'가 따로 존재한다. 차이라면 array는 선언시 길이가 정해져서 변할 수 없는 반면 slice는 길이가 변할 수 있다는 것이다. 사실 내 기준으로 실무에서 array를 쓰는 경우는 거의 못 봤다. - JavaScript 등을 비롯해 forEach, map, filter등의 고차함수를 자체적으로 지원하지는 않아서, 대체로 반복은 `for`문에 의존해야한다. 이 부분은 솔직히 너무 옛날 C 냄새가 강해 불편하긴 하다. 만약 map을 구현한다 치면(~~근데 할 일 없음~~), 콜백함수의 리턴타입(`type ... struct`)을 미리 정해야 한다는 강타입 언어의 제약도 귀찮을 수 있다. 물론 정말 필요하다면 forEach, map 등을 메서드로 갖는 커스텀 slice (또는 array) 구조체를 만들어 사용할 수는 있다.(~~동료들의 멘탈 삭제~~) - 가비지 콜렉션(GC) 기능이 있는 managed 언어이다. [Discord의 경우는 Go를 쓰다가 GC 때문에 latency spike이슈가 있어서 Rust로 옮겼다는 글](https://discord.com/blog/why-discord-is-switching-from-go-to-rust)도 있는데, 그정도로 성능이 크리티컬한 앱이면 당연히 unmanaged 언어를 선택하는게 맞다. 그러나 일반적인 범용 API 서버의 경우 Go는 경쟁군의 도구들 대비 충분히 높은 성능과 가용성을 제공한다. 경쟁군이 대체로 스크립트 언어들이니 컴파일 언어인 Go가 성능상 이기고 들어가는 것은 어찌보면 당연하다. - 위 이야기의 연장선으로, 힙/스택 무엇을 사용할지를 프로그래머가 관리하지 않는다. 예를 들어 go에서 struct를 생성하는 예시를 살펴보자. ```go type Person struct { Name string Age string } // 방법 1 p1 := &Person{name: "Chris", Age: 80} // 방법 2 p2 := new(Person) p2.Name = "Chris" p2.Age = 80 ``` go에서는 새로운 struct 인스턴스를 생성할 때 위와 같은 방법들을 사용하는데, 코드를 보면 마치 Java에서 `new`로 객체를 생성하거나 C 언어에서 `malloc`을 하는 느낌이라 웬지 heap에 할당될 것 같지 않은가? 그러나 go에서는 Escape Analysis라는 과정을 통해 선언된 변수를 힙에 할당할지 스택에 할당할지를 컴파일러가 알아서 결정하고 사용이 끝나면 GC를 통해 자동으로 회수한다. 선언된 변수가 함수 내에서만 사용된다면 **스택**에, 포인터로 리턴된다면 함수가 종료된 후에도 다른 곳에서 사용될 여지가 있으므로 **힙**에 할당된다. ## 마치며 연봉도 높은 언어이고 해외는 물론 국내에서도 일자리를 찾아보면 은근히 있으니 공부해두면 좋다. 채널(`chan`)이라는 문법과 개념이 좀 어려운데, [gorilla websocket 예시](https://github.com/gorilla/websocket/blob/main/examples/chat/hub.go)를 보면 이해하는데 도움이 된다. (코드만 봐서는 어렵고, 실제 로컬에서 실행해보면 확 와닿는다.) 본인이 서버 개발자이고 이미 주력으로 Java, Kotlin, Nodejs, Python, Ruby 등을 익숙하게 다룬다면, go 역시 기술셋에 추가하기를 강력히 권장한다. 작은 서버를 여러개 띄우는 MSA 형태의 서비스를 구성할 때 매우 좋은 선택지일 수 있다.
비트코인 블로그
2024-08-09
1
1010
KISS OF LIFE - Sticky (2024) 리뷰
간만에 무한반복 중인 곡이 생겼다. 일단 곡이 정말 잘 뽑혔고 뮤비나 음악의 전반적인 디렉팅의 디테일 하나하나도 너무나 좋다. ## 뮤직비디오 [![KISS OF LIFE(키스오브라이프) 'Sticky' Official Music Video](http://img.youtube.com/vi/IajeQM00yfE/0.jpg)](https://youtu.be/IajeQM00yfE) ## 리뷰 ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/fe6e2445-2e7c-491c-8099-794356f9ae20_image.png) 우아하게 걸어나오는 벨. 하늘색과 핑크색이 굉장히 잘 조화된다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/f8f763a6-a875-4354-9336-45c5de1e519c_image.png) 청량함을 가득 머뭄고 시작되는 "How long before we fall in love?" 도입부가 순식간에 청자들의 귀를 사로잡는다. 전반적으로 II - V - I - VI 코드가 반복되는 이지리스닝. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/bf40f7a7-b0ea-4763-a50d-73c129b740d3_image.png) 뮤직비디오 말고 퍼포먼스 버전의 안무에서는 이렇게 쥴리와 하늘이 서로 바라보는 안무로 시작되는데, 이 버전도 굉장히 귀엽다. (~~[가끔 쥴리가 메롱하기도 한다](https://www.youtube.com/shorts/JkO67x8Xxes)~~) ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/64c73632-2773-4b71-bda8-b7bfcfe55e65_image.png) 논란의 쥴리 자전거 씬 ([~~본인피셜: 누가 자전거를 그렇게 타냐~~](https://community.fanplus.co.kr/kissoflife/53578741?page=1&from=community)) "won't you navigate me!" 이 가사도 굉장히 로맨틱하게 느껴진다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/5d370493-7830-4e77-93c7-26c3e0111522_image.png) 사진 두번째의 청바지 입은 남자 백댄서분 뭔가 일론 머스크 닮은듯 ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/44b2737b-5e03-4b97-a0f7-4a26436cba25_image.png) 벨 단독 파트. "Oh, 뛰어들**어** 둘만의 ocean dive 왠**지** 묘한, 이 느낌 **녹아**내려, sticky, sticky, sticky, sticky (ayy, ayy)" 자연스런 성구전환을 통해 볼드체로 표시된 가성 부분과 진성을 넘나드며 섬세한 다이나믹 변화로 프레이즈를 매우 아름답게 표현해냈다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/34294450-97d5-40c8-b2c7-a762cb647f44_image.png) 2024 워터밤에서 남성 청중들을 열광시켰던 장면. 쥴리를 중심으로 다 같이 윗옷을 벗어던지는 안무를 하는데, 실제 무대에서는 실수할 확률이 생각보다 큰 부분일 수 있겠다는 생각이 든다. 쥴리나 나띠, 제이팍 등 뛰어난 댄서들을 보면 여유있고 행복한 미소 등 표정으로 표현하는 부분 역시 표현력의 거의 5할 이상은 되지 않나 싶다. 프로 댄서가 더더욱 프로인 느낌을 확 줄 수 있는 부분이라고 본다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/062b68f7-5a48-474d-9021-bd8ad0eacae0_image.png) Sticky 챌린지의 핵심인 트월킹. '트월킹' 하면 보통 저속함을 떠올리게 되지만, 개인적으론 이 경우는 그런 느낌은 들지 않는다. 정말 멋있다. 이런 느낌 트월킹의 원조라 할 수 있는 [Tyla의 Water](https://www.youtube.com/watch?v=XoiOOiuH8iI) 이후로 르세라핌의 Smart도 그렇고 꽤 자주 보이는 것 같다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/2f0e36fa-d9e0-4901-a162-966d97c93f53_image.png) 막내 하늘의 단독 파트. 벨과는 또 다른 느낌의 단단한 벨팅으로 색다른 맛을 주었다. 살인미소로 자꾸 내 시선을 강탈하는 우측 끝의 일론 머스크 ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/ae67a93b-e348-4cc1-be52-14f08571aef7_image.png) 다시 벨의 단독 파트. "오렌지빛 물든 저 노을에 kiss it, so say yeah" 높은 음역대에서 빠르고 정확하게 여러 음들을 짚어내는 훌륭한 Vocal Agility를 갖고 있다. 이런 테크니컬한 riffs & runs의 세계구급 초고수들로는 나얼, Mariah Carey, Ariana Grande, Wanya, Tonex, Terrell Carter 등의 정통 R&B 테크니션들이 있다. 화려한 기교에 관심이 있다면 이런 뛰어난 보컬들의 곡들을 카피해보는 것도 좋다. (~~if you can~~) ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/609011f7-1750-4bce-ab36-032689e84cf2_image.png) Sticky, sticky, sticky, sticky (say yeah, yeah, oh, whoa) And you're the one who's with it, with it, with it, with me 양쪽으로 팔을 찌르는 이 안무도 개인적으로 좋아서 넣어보았다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/6c074210-ed32-4e34-9df8-7113e9c2a9eb_image.png) 쥴리 단독 랩 파트. Yeah, 느려져도 돼 like slo' mo-motion (motion) I'd take you to the new world, so, boy (new world) 발 닿는 대로 가, there's no way 그리곤 내게로 와, follow my lead (oh), let's get it, yeah 비교적 자유롭게 노는 분위기의 컨셉으로 디렉팅된 것 같은 이 장면도 정말 좋다. 분위기가 고조되며 다시 싸비로 들어간다. ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/bff7b545-c49c-4277-8fb4-96dd405445b9_image.png) 이 부분 안무 진짜 어려워보인다. 보기에는 별거 아닌 것 같은데, 잘 생각해보면 저렇게 한쪽 다리에서 다른쪽 다리로 물결치듯 4회 튕기는 느낌의 모양이 나오려면 꽤나 복잡할 것 같다. (쥴리 참고) ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/566de479-a1ca-4557-9009-23a61eaff6a2_image.png) ![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/2043c0a1-646a-4a91-942b-06aabb70ea40_image.png) 나띠 단독 파트. I like it, 눈부신 여름빛 (oh, oh) 지금 우린 sticky (sticky), sticky, sticky (yeah), sticky (wait, wait, wait) "sticky" 단어를 끝낼때마다 살짝 나띠 특유의 삑사리나는 느낌으로 끝내주는 것도 매력 포인트. (~~벨이 혀를 살짝 내미는 것도 안무의 일부임~~) 최종본에서 카메라에 튄 물을 그대로 두기로 결정한 것도 현장감을 살려준다. ## 총평 드라이브하며 신나게 들을 수 있는 2분 40초 가량의 이지리스닝으로, 선배 세대의 여름 여제인 씨스타가 떠오른다는 사람들도 있다. 쥴리 나띠 벨 하늘 네 명 다 너무 예쁘게 나왔고 뮤직비디오도 정말 예쁘지만, 굳이 뮤비가 없더라도 **곡 자체가 워낙 잘 뽑혀서** 한동안은 더 계속 듣게 될 것 같다. FIFTY FIFTY - Cupid의 사례를 봐도 그렇고, 곡이 일단 좋아야 뜰 수 있다. 나는 네 명 중 [이 영상](https://www.youtube.com/shorts/dJZyVL9ctIw)으로 나띠를 제일 처음 알게 됐는데, 눈이 휘둥그레지는 난이도의 안무를 초딩 율동마냥 갖고 노는 놀라운 실력을 지닌 나띠의 영상을 좀 더 찾아보다가 아래의 영상을 보게 됐다. [![나띠 & 제이팍 - Sticky 챌린지](http://img.youtube.com/vi/EJBZA3nFAY0/0.jpg)](https://www.youtube.com/shorts/EJBZA3nFAY0) (~~제이팍 왜 안 늙음(?)~~) 보다가 곡이 귀에 확 꽂혀서 뮤직비디오를 찾아 보게 됐고 심지어 이 글까지 쓰게 되었다. 음악을 마케팅 하는데 있어서 숏폼 챌린지가 얼마나 강력한 위력을 지녔는지 새삼 생각하게 된다. 네 명 다 정말 개성 넘치고 실력도 좋아서 앞으로의 미래가 크게 기대된다. 인성보다 중요한 것은 없다는 것을 생각하면서 사건사고 없이 무탈하게 떡상했으면 좋겠다.
비트코인 블로그
2024-07-08
0
1615
구글 Gemini 1.5 flash를 이용해 스트리머 선물 포지션 입력 자동화하기
운영중인 사이트 [코인충](https://coinsect.io/)에는 박호두, 사또, 짭구 등 유명 스트리머들의 포지션을 거의 실시간에 가깝게 계속 추적해서 보여주는 기능이 있다. ![실시간 포지션](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/321667ca-4e6a-45eb-85d1-b172718f2b2a_image.png) 코인충 관리자 페이지의 '실시간 포지션' 메뉴 ![재래식 모니터링]( https://d1085v6s0hknp1.cloudfront.net/boards/free_board/d28bd415-b424-4180-baf2-06e05557c7a6_image.png) 모니터링은 재래식으로 이루어진다 영양가도 없는 이들의 포지션을 입력하려면 일일히 그들의 방송을 보면서 직접 입력하는 수 밖에 없는데, 내 사이트를 찾는 사람들이 피크 시간대(밤 10:00~)에는 무려 4~500명에도 달하기 때문에 그들을 위해서 이 수고를 감수하고 있다. 그렇다. 정말 위처럼 여러 스트리머들의 방송을 동시에 띄워놓고 실제 보면서 포지션을 업데이트하는 것이다 [...] 사실 이 수고를 덜기 위해 자동화를 하려는 생각은 오래전부터 하고 있었다. 처음 시도했던 방법은 이미지 인식 API를 이용해 화면에서 특정 위치의 숫자들을 추출해서 거래소별로 어떤 값이 entryPrice인지, liqPrice인지 등을 다 분석해 시행착오를 거치며 파싱하는 구현을 했었으나, 스트리머들이 화면을 매번 완전히 똑같이 띄우지도 않을 뿐더러 거래소들도 한 번씩 업데이트를 하며 UI가 달라지는 것을 내가 따라가기도 귀찮아 결국 때려쳤었다. 그런데 LLM이라는게 등장했고, chatgpt나 gemini를 통해 멀티모달로 테스트해보면 어떤 거래소이든 상관없이 상당히 높은 정확도로 원하는 형태의 JSON을 응답받을 수 있었다. ```javascript { entryPrice: number, liqPrice: number, size: number, contract: string, } ``` 그러나 역시 API 비용이 공짜가 아니었기 때문에, 굳이 여기에다가 돈을 쓰고 싶지는 않았다. 그런데 얼마 전 구글이 [gemini-1.5-flash](https://ai.google.dev/gemini-api/docs/vision?hl=ko&lang=node#prompt-multiple) API를 무료로 풀었다. 적용해보니 상당히 잘 되어서 바로 사용하게 되었다. responseMimeType을 json으로 고정할 수 있는 점도 장점인데, 이게 되지 않는다면 이론상 꼭 JSON으로 응답받을 수 있다는 보장이 없기 때문이다. 프롬프트를 포함한 코드는 아래와 같다. ```javascript autoParse: async (url: string) => { const genAI = new GoogleGenerativeAI(process.env.GOOGLE_AI_STUDIO) const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash', generationConfig: { responseMimeType: 'application/json', }, }) const result = await model.generateContent([{ text: ` Fill this JSON using given image. Useful numbers are located at the bottom of the image. You can guess what is 'entry' and what is 'liq' by looking at the numbers, with a given position size (could be positive or negative). { "entryPrice": number, // some platforms say it as 'open' price. "liqPrice": number, "size": number, // Negative if it looks like a short position, positive if it looks like a long position. "contract": string // e.g. 'BTCUSDT' | 'ETHUSDT' ... ends with 'USDT'. If you're not sure, just put 'BTCUSDT'. } `, }, { inlineData: { mimeType: 'image/png', data: await helpers.imageUrlToBase64String(url), }, }]) return result.response.text() }, ``` 사람이 이미지를 보고 정보를 인식할 수 있으면 LLM도 나 못지 않게 잘 할 수 있다는 것이 느껴질 정도로 상당히 정확도가 높다. 비용이 무료이기 때문에, 실시간 스트리밍 중인 라이브 URL만 입력해두면 주기적으로 알아서 그 방송의 현재 화면을 스냅샷으로 떠와서 위 API까지 요청하는 크론잡을 구현해두면 내가 아예 여기서 손을 떼도 되지 않을까 싶다.~~어떻게 AI가 인건비를 줄이고 일자리를 없애버리는지 피부로 느껴진다~~
비트코인 블로그
2024-07-04
0
780
바닐라 자바스크립트가 반응형을 지원한다?!
자바스크립트에 네이티브로 반응형 값을 선언할 수 있는 방식을 추가하자는 [제안](https://github.com/tc39/proposal-signals)이 있다. 아직 Stage 1이며, [폴리필](https://github.com/proposal-signals/signal-polyfill)까지 구현이 완료된 상태이다. 개인적으로 정말로 크게 기대되는 부분이다. 반응형 프로그래밍은 오랜 역사를 가지고 있다. 아마 스프레드시트의 수식이 대중적으로 가장 유명하지 않을까 싶다. ![스프레드시트와 반응형 프로그래밍](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/b3709436-41c2-42f1-96e6-ba896ac12ffd_image.png) 스프레드시트와 반응형 프로그래밍 위 사진에서 A1의 내용을 수정하면 A2의 내용도 즉시 그에 맞추어 바뀐다는 것은 우리에게 익숙하다. 이처럼 반응형 프로그래밍은 우리가 여러 값들을 선언하고 서로의 관계를 정의하면, 하나가 변경될 때 즉각적으로 모두가 반응하는 매우 편리한 기능을 제공한다. 이를 통해, 한줄 한줄 뭘 해야할지 절차적으로 명시할 필요 없이 선언만 하면 런타임이 알아서 상태를 관리하는 프로그래밍을 할 수 있게 되어 프로그래머의 삶의 질이 올라간다. 모던 자바스크립트 툴들은 이런 반응형 프로그래밍을 할 수 있는 도구들을 자기들 나름의 방식으로 제공한다. ```javascript // React const [count, setCount] = useState(0) const double = useMemo(() => count * 2, [count]) // Vue const count = ref(0) const double = computed(() => count.value * 2) // Svelte let count = 0 $: double = count * 2 // Solid const [count, setCount] = createSignal(0) const double = createMemo(() => count() * 2) ``` 사용하는 툴에 따라 같은 목적을 완수하기 위한 여러가지 방법이 존재하는 것은 생태계의 파편화를 초래하기 때문에 좋지 않다고 생각한다. 만약 바닐라 자바스크립트가 네이티브로 반응형 프로그래밍을 지원한다면 위와 같은 다양한 구현체들은 이제 필요 없어질 것이며, 기존 코드와의 호환성을 위해 각 툴들이 여전히 위의 방식을 지원하더라도 아마 내부 구현은 자바스크립트 네이티브 시그널을 사용하는 방식으로 변경하게 되지 않을까 생각된다. 제안된 TC39 프로포절에 따라 위 상황을 구현해보면 다음과 같다. ```javascript const count = new Signal.State(0) const double = new Signal.Computed(() => count.get()) ``` 위 문법이 가장 편리한 shorthand인지는 모르겠지만, 어쨌든 목적을 이루는 방법이 한 가지로 통일된다면 좋겠다. 눈여겨볼 점들이 몇 가지 있다. - 종속성을 알아서 추적하기 때문에 React처럼 종속성 배열을 넘겨줄 필요가 없다. - Lazy Evaluation을 하기 때문에, 선언되었을 때가 아니라 호출될 때 실행된다. - Memoization을 한다. Vue의 반응형 시스템에서 상당한 영감을 받은 것이 보인다. 또한 특정 값이 변경되었을 때 콜백을 트리거하는, 흔히 말하는 'effect' 구현을 위한 Watcher의 개념에 대해서도 제안하고 있다. ![Thanks for inventing Javascript](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/6c78fccd-d0a6-40b9-9346-b2881cef41b2_image.png) "Thanks for inventing Javascript" 위와 같은 밈으로 희화화되던 자바스크립트가, 이제는 가장 많은 프로그래머에게 사랑받는 언어 중 하나로서 시대의 선두에 서서 패러다임의 진화에 빠르게 발맞추어나간다. 세상이 참 빠르게 변한다는 것을 느낀다.
비트코인 블로그
2024-07-01
0
848
SEO 이야기 #2 - 고려해야 할 요소들
그야말로 생성형 AI의 시대이다. 이제 **좋은 검색 결과를 얻기 위한 핵심 키워드**를 잘 입력하는 것에 목을 멜 필요가 없다. 검색엔진이 아닌 [ChatGPT](https://chatgpt.com/)나 [Gemini](https://gemini.google.com/)에 접속해 전문가와 대화하듯 자연어로 '실제 질문'을 하면 된다. 그러면 AI들은 자기들이 인터넷을 뒤져서 최적의 답변을 해준다. 온 마켓이 그야말로 bloodbath였던 22년 말 혜성처럼 등장한 ChatGPT는 인공지능에 대한 엄청난 하이프를 몰고 왔고, 아주 깊은 폭의 조정을 받고 있던 나스닥의 추세를 뒤집는데 공헌했다고까지 말해도 지나치지 않을 것이다.~~everything is going to zero against $NVDA~~ 우리가 그동안 알고 있던 챗봇들이란게 심심이 같은 수준이었으니, 무려 변호사 시험에서까지 높은 점수를 따내는 이 미친 성능의 LLM들을 보면 실로 감탄을 금할 수 없다. 최근 몇 년간의 AI의 발전속도를 보면, '인터넷정보검색사'와 같은 자격증이 있던 시대는 말할 것도 없고 매달 격세지감을 느낄 수준이다. 사설이 길었다. 어쨌든 그럼에도 불구하고 결국 이 챗봇들이 참조하는 검색엔진들이 수집하는 데이터는 당신의 웹사이트이다. 또 LLM들도 결국은 실세계의 데이터를 가져오기 위해 검색엔진에 질의한다는 것을 생각하면, 당신의 업종에서 중요한 키워드들에 대해 상위권의 검색 결과를 차지하는 것의 중요성은 여전하다. 그럼 이 목적을 어떻게 달성할 수 있을까? --- ### SSR 가장 먼저 챙겨야 할 것은 [Server-Side Rendering](https://blog.coinsect.io/posts/seo-ssr-spa-universal-rendering)이다. 검색엔진이 당신의 사이트를 잘 읽을 수 없다면 여러가지 SEO 기법들을 아예 사용조차 할 수가 없다. 따라서 이것부터 먼저 구비해두자. 워드프레스 등의 유명 웹사이트 빌더들을 사용해서 비즈니스를 운영하는 중이거나 PHP, JSP 등을 사용하고 있다면 이 부분은 크게 신경쓸 것이 없으나, React 등의 자바스크립트 기반 모던 프론트엔드 툴들을 사용할 계획이라면 신경써야 한다. --- ### 백링크 당신이 아무리 마켓셰어 100%를 가져갈 만한 완벽한 웹사이트를 만든다 하더라도, url이 알려져 있지 않으면 딥웹이 되어버린다.~~존재하는지 아무도 모름~~ 따라서 사이트를 만들었으면 구글이 이미 인덱싱하고 있는 유명 사이트들에 내 사이트의 URL을 올려 긁어갈 수 있도록 해야 한다. 유명 SNS 들의 프로필이나 레딧 등의 커뮤니티에 링크를 노출하는 방법 등이 있다. 구글의 랭킹 알고리즘을 알 수는 없지만 백링크 수와 랭크 사이에 상관관계(!= 인과관계)가 있지 않을까 생각된다. --- ### 메타정보 이 글을 쓴 주요 목적이다. 당신은 아마 아래와 같은 카드에 익숙할 것이다. ![슬랙, 카카오톡의 메타 카드](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/2273f0e1-1e94-44b7-92eb-f47295d087c5_image.png) 슬랙, 카카오톡의 메타 카드 페이스북, 트위터, 카카오톡, 슬랙, 텔레그램 등 SNS나 메신저들에는 메시지에 URL이 포함되어 있으면 위 스크린샷과 같이 카드 형태로 해당 URL의 내용을 보여주는 기능이 있다. 눈길을 확 끌기 때문에 URL만 보일때보다 훨씬 더 클릭하고 싶어진다. 그런데 이것은 자연히 되는 것이 아니라, 해당 HTML의 `<head>` 안에 `<meta>`태그로 메타정보를 정의해두었기 때문에 보이는 것이다. 그럼 이처럼 메타 카드를 노출하려면 무엇을 작성해야하는지 살펴보자. ```markup <html> <head> <title>사이트 제목</title> <!-- 브라우저 타이틀 --> <meta name="title" content="사이트 제목"> <meta name="description" content="사이트 설명"> <meta property="og:title" content="사이트 제목"> <meta property="og:description" content="사이트 설명"> <meta property="og:image" content="사이트 이미지 URL"> </head> ``` 위와 같이 작성하면 된다. 여기서 `name` 속성과 `property` 속성이 함께 사용되고 있다는 점에 유의해보자. `name` 속성은 일반적인 정보 공개 차원에서 들어가는 정보라면, `property`는 [오픈그래프 프로토콜](https://ogp.me/)에서 정의하는 방식으로서 카드 형태로 노출하려면 **반드시 따라야 하는 규약**이다. 플랫폼에 따라 이 규약이 지켜지지 않은 경우 fallback으로 다른 메타태그를 파싱해서 쓰는 경우도 있을 수는 있지만, 대부분의 플랫폼에서는 `<meta property="og:title|description|image" content="값">` 형태로 정확히 써야지만 올바로 카드가 노출된다. 즉, 아래와 같다. ```markup <meta name="title" content="인식 못함"> <meta property="og:title" content="인식함"> <meta name="description" content="인식 못함"> <meta property="og:description" content="인식함"> ``` [X(구 트위터)의 경우](https://developer.x.com/en/docs/twitter-for-websites/cards/guides/getting-started)는 `<meta property="twitter:title">` 등 `twitter` prefix를 사용하면 된다. 카드를 보여주고 싶은 플랫폼의 권장사항을 따르면 되는데, 대체로 `property`와 `og:필드` 조합이면 충분하다. title이나 description은 너무 길지 않도록 적당히 쓰는 것이 좋다는 이야기가 있다. image의 경우는 용량이 너무 크면 로딩하는데 시간이 지나치게 걸릴 것이므로 주의가 필요할 것이다. 여담으로 디시인사이드나 블라인드같은 커뮤니티들의 경우, 글에 첨부된 이미지가 없을 경우 사이트 대표이미지를, 1개 이상일 경우 첫째 이미지를 `og:image`로 사용하는 것을 볼 수 있다. --- ### 사이트맵 사이트맵이란 말그대로 사이트에 어떤 페이지들이 존재하는지를 나열한 XML 파일이다. 보통 웹사이트의 루트에는 `robots.txt`를 두는데, [이 블로그도 마찬가지이다.](https://blog.coinsect.io/robots.txt) 보면 [Sitemap](https://blog.coinsect.io/sitemap.xml)의 위치를 명시해두고 있다. 클릭해보면 이 블로그에 존재하는 모든 페이지를 나열해둔 것을 볼 수 있다. 이 파일을 작성하면 검색엔진 등의 크롤러가 이 웹사이트에 어떤 페이지들이 존재하는지를 쉽게 파악하도록 도움을 줄 수 있다. 정적인 방식으로 직접 XML 파일을 작성해도 되고, `GET /sitemap.xml`로 요청이 들어왔을 때 동적으로 만들어 응답할 수도 있다. (e.g. [Remix 예시](https://dev.to/chrisbenjamin/tutorial-add-sitemapxml-and-robotstxt-to-remix-site-4n23)) --- ### 가장 중요한 것? 뭐니뭐니해도 SEO에서 가장 중요한 것은 컨텐츠의 퀄리티이다. 구글링을 한 사람들이 최종적으로 유입될만한 연관성이 높은 컨텐츠를 만드는 것이 가장 중요하다. 연관성이 높다면 그 페이지는 구글이 알아서 모신다. !['michael saylor mbti' 구글링 결과](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/aa19211a-62ec-4bc9-a6a4-d9392fa2f81f_image.png) 'michael saylor mbti' 구글링 결과 여기서 [최상단에 노출되는 사이트](https://www.personality-database.com/pt/profile/265652/michael-saylor-mbti-personality-type)를 클릭해서 들어가보면, 놀랍게도 제대로 된 메타 태그 하나 없고 페이지 소스 보기로 HTML을 확인해보면 하단에 그냥 `<div id="root"></div>`만 달랑 있는 순수한 SPA임을 확인할 수 있다. 즉, 검색엔진 대응이 전혀 되어 있지 않은 페이지인데도 1순위로 랭크되는 것이다. 결론적으로 가장 중요한 것은 **컨텐츠의 연관성**임을 알 수 있다. 그렇다고 SEO를 소홀히 해도 괜찮다는 것은 아니다. 메타정보를 잘 넣는다면 메타카드 파싱을 지원하는 플랫폼들에서의 사람들의 클릭율을 분명 높일 수 있고, 검색엔진이 사이트를 파악하고 인덱싱된 결과를 노출하는데도 분명 큰 도움을 줄 수 있다.
비트코인 블로그
2024-06-30
0
611
왜 프로덕션에서는 console.log를 제거해야 할까?
프론트엔드 코딩을 하다보면 `console.log`를 넣을 일이 매우 많다. 나는 특히 크롬의 디버깅 툴을 쓰기보다는 필요한 곳들에 로깅을 하면서 개발하는 것을 선호한다. 그런데 자바스크립트는 함수가 실행될 때 주변의 환경을 기억하는 클로저라는 기능이 있다. 이 때문에, 참조되는 변수는 메모리에서 GC의 대상이 되지 않고 남아있게 된다. 아래의 상황을 생각해보자. ```javascript const myArr = new Array(10000000).fill('some large data'); const foo = (a: number, b: number) => { console.log(myArr) return a + b } ``` 위 경우, myArr의 크기는 약 150MB이다. (1000만 * 15 bytes) 저 변수를 사용하던 다른 곳들에서 참조가 다 끝나고 나면 GC의 대상이 된다. 그러나 위 코드처럼 `console.log(myArr)`로 참조하게 되면 프로덕션에서 150MB의 용량을 불필요하게 차지하게 된다. 이런 메모리 낭비를 비롯해 아래의 문제들도 있다. - 노출하기를 원하지 않는 정보를 노출하게 될 수 있음 - 코드가 지저분해짐 console.log는 사용한 뒤 즉시 삭제해주는 습관을 들이자.