logo
logo
프론트엔드
Relay
GraphQL 기반의 데이터 중심 React 애플리케이션을 구축하기위한 JavaScript 프레임워크이며, Facebook에서 제공/유지 보수를 하고 있다.
StackOverflow 질문 수: 749
Github Stars : ★ 18570
사용 기업
종합
소셜/컨텐츠
기타
techstack-logo
네이버
techstack-logo
당근
techstack-logo
그린랩스
기술 블로그 글
SK텔레콤
브라우저 기반 IPFS 네트워크 동향 (2025년 1월 기준)
브라우저 기반 IPFS 네트워크에 관심이 많습니다.웹브라우저만으로 IPFS 네트워크를 구축할 수 있다면, 서버 없는 서비스(웹3 서비스)가 가능해질 것입니다.IPFS 블로그(https://blog.ipfs.tech/)에서 이와 관련된 기사를 엄선하여 요약합니다.• None SPA 또는 MPA 형태로 개발된 Dapp은 IPFS로 쉽게 배포할 수 있다• None Helia는 브라우저를 IPFS 노드로 만들어주는 라이브러리다• None Helia가 브라우저에서 제공하는 기능은 'CID 데이터 관리'와 'CID 데이터에 대한 Verified Retrieval' 2가지다• None CID 데이터 관리: 데이터를 CID 데이터로 만들고 해석하는 기능을 제공한다• None CID 데이터에 대한 Verified Retrieval: CID로 지정된 데이터를 Bitswap 또는 IPFS Gateway를 통해 가져올 수 있다• None 브라우저 IPFS 노드는 보통 수명주기가 짧기 때문에, CID 데이터를 업로드할 때 pinning 서비스를 이용하거나 직접 운영하는 IPFS 노드(서버)를 이용하는 것이 좋다• None IPFS Gateway는 브라우저에서 IPFS 데이터를 가져올 때 특히 유용한 기술이다• None IPFS 데이터에 대한 검증(Verification: IPFS Gateway가 전송한 데이터가 내가 요구한 그 데이터가 맞는가에 대한 검증)을 브라우저가 수행한다면, 브라우저는 IPFS Gateway를 신뢰하지 않더라도 문제 없이 이용할 수 있다 (Trustless Gateway 사용이 가능하다)• None 이를 위해 Interplanetary Shipyard팀(프로토콜랩으로부터 분사한 개발조직)이 @helia/verified-fetch 라이브러리를 개발/배포한다• None Shipyard팀의 다음 목표는 WebRTC와 WebTransport 프로토콜을 이용해서 브라우저에서 직접 Kubo IPFS 노드와 통신하는 것이다• None 우리(Interplanetary Shipyard)가 관심을 갖는 주제는 웹에서 IPFS를 사용하는 것이다• None 다시 말해 웹브라우저에서 다른 IPFS 노드에 연결할 수 있게 만드는 것이다• None 이를 위해 다음과 같은 프로젝트를 진행하고 있다• None Verified Fetch: 브라우저의 fetch API와 유사한 API를 제공, 이를 통해 IPFS 데이터를 검증/수신하는 기능을 제공한다• None Browser Transport: 브라우저에서 사용할 수 있는 WebRTC와 WebTransport 프로토콜을 기반으로 외부 IPFS 노드와 통신하는 기능을 제공한다• None AutoTLS: 브라우저는 보안 통신을 위해 CA 공인 인증서를 요구하나 IPFS 노드는 통상 인증서 없이 운영된다. AutoTLS는 이 갭을 메꾸는 기능을 한다• None Delegated Routing: 브라우저가 IPFS 기능을 호출할 때 이용할 수 있는 https://delegated-ipfs.dev/routing/v1 엔드포인트를
nodejs
relay
webrtc
리디
transactional outbox message relay 개선하기
안녕하세요. 리디 서비스백엔드팀 강규입니다.지난 글에서 Transactional Outbox 패턴을 사용해 메시지 발행을 보장하는 message-relay를 리디에서 어떻게 운영하고 있는지 소개했습니다.오늘은 message-relay를 운영하면서 겪은 다음 이슈를 개선한 내용을 소개하겠습니다.• 많은 양의 메시지가 message 테이블에 입력되는 상황에서 처리된 메시지의 삭제가 지연되었을 때 select 쿼리의 latency가 저하되는 문제• message 테이블에 대한 select for update 쿼리와 delete 쿼리의 latency가 간헐적으로 치솟는 문제기존에 message-relay는 처리할 메시지가 기록된 테이블로부터 메시지를 읽어서 kafka에 발행하고 테이블에 처리된 메시지 id를 기록하는 방식으로 동작했습니다. 이러한 구조에서는 message-relay가 처리할 메시지를 가져올 때, 아래의 쿼리가 사용됩니다.를 이용해서 쿼리 실행 계획을 확인해 봤습니다.nested loop anti join 방식으로 조회하면서, driving 테이블은 , driven 테이블은 테이블로 결정된 것을 알 수 있습니다. 테이블 row를 하나씩 읽어가면서 조건으로 테이블 row를 찾고 조건에 해당하는지 확인합니다.anti join이기 때문에 해당 조건을 만족하지 않는 row가 많을수록 실행 시간이 늘어날 것을 짐작할 수 있습니다. 테이블에 있는 처리된 메시지 삭제가 비동기로 동작했기 때문에 처리된 메시지의 삭제 속도가 메시지가 새로 쌓이는 속도보다 느려질 수 있었습니다.즉, 많은 양의 메시지가 테이블에 쌓여서 조건을 만족하는 row가 늘어나면 위 select 쿼리의 성능이 악화될 수 있었습니다.nested loop join으로 인해 성능 저하될 수 있는 것을 개선하기 위해서 left join을 제거하고 테이블 하나만 사용하도록 수정이 필요했고, 아래의 방법을 검토했습니다.• 처리된 메시지가 다시 처리되지 않도록 테이블에 column을 추가해서 메시지의 처리 상태를 변경해 두고 비동기적으로 삭제하는 방법• 처리가 완료된 메시지를 동기적으로 곧바로 삭제하는 방법처리가 완료된 메시지를 유지할 필요가 없기도 하고, 곧바로 삭제해도 delete 쿼리의 lock wait 문제가 발생하지 않도록 개선할 수 있게 되어 후자를 선택했습니다. delete 쿼리의 lock wait 개선과 관련된 내용은 이번 글에서 소개됩니다.결과적으로 테이블 하나만 사용하게 되면서 테이블 row의 개수에 영향을 받지 않고 일관된 select 쿼리 성능을 유지할 수 있었습니다. 또한 처리된 메시지의 삭제를 동기로 실행하게 되면서 전반적인 코드도 간결해졌습니다.가용성을 위해 message-relay node를 2대 운영하고 있기 때문에 테이블 row에 적절하게 lock을 설정해서 동일한 메시지에 대해서 중복으로 처리되지 않도록 하는 것이 중요합니다. 기존에는 MySQL record lock 용도로 분리된 테이블(이하 테이블)에 exclusive lock을 설정해서 동시성을 제어
mysql
relay
매스프레소
Aurora/RDS 전문가의 GCP Cloud SQL이전기
Aurora/RDS 전문가의 GCP Cloud SQL 이전기우당탕탕 Cloud DB 이전기새로운 도약을 위한 모험은 항상 과제와 함께합니다. 저에게 Aurora MySQL/RDS for MySQL 전문가로서의 경험이 GCP라는 새로운 cloud platform 운영에 대한 도전으로 다시 다가왔습니다. 이번 글에서는 public cloud 간의 이전 과정을 우당탕탕 Cloud DB 이전기 로 공유하여 public cloud 간 이전 혹은 on-premise와 public cloud 간 이전을 고민하는 개발자들에게 조금이나마 도움을 주고자 합니다.1. 목표AWS에서 GCP로의 크게 4가지 주요 DB 작업을 목표로 가지고 진행하게 했습니다.Aurora MySQL/RDS for MySQL/DynamoDB GCP Cloud SQL for MySQL 이전{1 DB : n Application} Architecture {1 DB: 1 Application} Architecture전환(DEV 환경)service 안정성 향상과 Cost 절감을 위한 query, schema 및 index 최적화MySQL version upgrade이 모든 작업은 service downtime(10분 이내) 및 이전 비용 최소화라는 기본 목표 위에 설정했습니다. 실제로는 2분 내외의 service downtime이 예상되었으나 초보 GCP 운영을 생각하여 속도 조절을 위한 buffer 시간을 추가했습니다.특히 3번의 경우는 AWS에 선 적용 후 migration을 할 것인지 아니면 migration target(GCP)에만 적용할 것인지를 두고, 내부 논의를 하였고, 큰 작업에 대한 Aurora MySQL/RDS for MySQL의 temporary disk size 이슈와 현재의 query 를 검토하고 query, index 및 schema 변경에도 문제가 발생하지 않을 것이라는 자신감(?) 으로 후자를 선택하여 진행했습니다. 이 부분은 개발사마다의 환경(query 형태, DB spec, DB 구성 등)이 다를 수 있어 각 회사에 맞게 판단하시면 좋겠습니다.2. 사전 검토우리의 궁극적인 목표는 service downtime 및 이전 비용을 최소화하고, 이전 후에 장애가 없도록 하는 것이었습니다. 여기에, instance spec을 down scale하고, 개발자들의 투입 리소스를 최소화하는 것도 필요했습니다. 이를 위해 크게 5가지의 내용으로 검토했습니다.검토 전에 먼저 DB의 현재 상황을 파악하기 위해 제일 먼저 DB 실시간 모니터링 환경(grafana+victoriaMetrics+mysql/rds exporter)을 구축한 후, 아래의 검토 사항을 진행했습니다.Step 1: 실행 query검토service 실행 query를 분석하기 위해 general.log 를 이용해 실행 query를 추출하고 pt-query-digest 등등의 query generator를 통해 실행 query및 query 수행량 등을 파악합니다.Step 2: schema 및 DB parameter 검토주요 parameter 설정의 미설정 또는 오설정 여부 판단 및 schema type의 과설정 또는 오설정 판단 등등의 부하를 감소 시킬 수 있는 요소를 파악합니다.Step 3: replica 구축 소요 시간 검토6일 이내에 replication 구축이 완료되고 sync가 시작되어야 한다는 전제 조건을 만족해야 합니다.사용 비용이 발생하는 solution(GCP Cloud Datastream / AWS DMS)을 사용하지 않기 위해 검토해야 합니다.mysql export 시간 측정dump file 전송시간 측정mysql import 시간 측정Sync complete 시간 측정Step 4: version 차이에 따른 영향 검토Aurora MySQL 2.x는 MySQL 5.7.x 기반이기 때문에 MySQL 8.0.2x 와의 parameter및 optimizer 차이에 따른 영향도를 검토해야 합니다.Step 5: 사용 DB 종류의 단일화Aurora MySQL/RDS for MySQL/DynamoDB Cloud SQL for MySQL3. 검토 결과Step 1: 실행 query검토 결과query 실행량이 너무 많음. 예를 들어 SELECT 1 과 같은 query는 단일 query로는 부하가 되지 않으나, 모이면 부하가 됩니다. 또, join은 부하가 크다라는 오해가 있어 join을 사용하지 않고 각각 실행하는 경우 오히려 더 부하가 됩니다.single index가 많음. 이로 인해 scan량이 많고, 정렬이 늘어나게 됩니다.e.g)SELECT * FROM my_item WHERE user_id='test' AND expired_time > now() ORDER BY expired_time;의 index가ALTER TABLE my_item ADD KEY ix_userid_expiredtime(user_id, expired_time);가 아닌ALTER TABLE my_item ADD KEY ix_userid(user_id);ALTER TABLE my_item ADD KEY ix_expiredtime(expired_time);로 생성되어 있다.특히 admin site의 filter 조건 대응을 위해 column별 data 분포도와는 상관없이 모든 filter 항목별로 각각 single index를 생성한 경우도 있어 이 부분에 대한 검토가 필요했습니다.3. or query가 많음. 아래 예제 query에서는 expired_time에 대해 NULL 대신 default값(ex.9999 12 31)을 주어지면 or를 제거할 수 있습니다. 제거하지 않는다면 optimizer에 따라 expired_time index를 활용할 수 없게 됩니다.e.g)SELECT * FROM my_item WHERE id = 1 AND (expired_time > now() OR expired_time IS NULL); 4. order by single-index-column desc limit xx query로 인한 오동작 수행 query가 다수 존재함.(MySQL O
awsauroradb
awsdynamodb
mysql
relay
리디
Transactional Outbox 패턴으로 메시지 발행 보장하기
안녕하세요. 리디 백엔드 엔지니어 강규입니다.오늘은 Event Driven Architecture에서 메시지 발행의 신뢰성을 보장하는 Transactional Outbox 패턴을 소개하고, 이를 리디 서비스에 적용하며 느낀 바를 공유하고자 합니다.Transactional Outbox 패턴이란 무엇인가요?Event Driven Architecture를 따르는 서비스에서는 대개 Message Broker를 이용해 다양한 메시지(이벤트)를 publish(발행) 하고, 그에 연관된 작업을 비동기적으로 처리하여 시스템을 통합합니다.이때 DB 트랜잭션을 실행한 뒤 연관 메시지를 Message Broker에 publish 하게 되는데, 때로 메시지 publish가 반드시 완료되어야 하는 경우가 있습니다.리디 주문 기능을 예로 들어볼까요? 먼저 주문이 발생하면, 사용한 캐시·포인트 금액을 차감하고 상품을 지급하며 주문 완료로 상태를 바꾸는 DB 트랜잭션이 발생합니다. 그리고 Message Broker에 주문 완료 메시지를 publish 합니다.DB 트랜잭션은 DB 차원에서 원자성(atomicity)을 보장하므로 트랜잭션에 포함된 query들은 원자적으로 실행되지만, 대개는 DB와 Message Broker가 다른 기종이라 원자적인 처리가 불가능합니다.따라서 DB 상 주문 완료 처리가 되었더라도 Message Broker에 메시지를 publish 하는 데 실패할 수 있고, DB의 주문 완료 처리를 rollback 하기도 어렵습니다.이런 문제를 해결하기 위해 Transactional Outbox 패턴이 등장합니다. Outbox는 주로 웹 메일에서 ‘보낸 편지함’을 의미합니다. Outbox를 Message Broker에 publish 할 메시지로 대응해서 생각해 보면 패턴의 이름을 이해하는 데 도움이 됩니다.즉, Transactional Outbox 패턴은 Message Broker로 publish 하려는 메시지의 생성을 DB 트랜잭션에 포함시켜서 원자적으로 처리되게 하는 것을 의미합니다.리디에 Transactional Outbox 패턴을 도입한 배경지난번 ‘리디에서 Kafka를 사용하는 법‘에서 소개한 것처럼, 리디 서비스는 Kafka를 중심으로 통합되고 있습니다. 그리고 API, batch process, Kafka consumer 등 다양한 서비스들이 데이터 영속화를 위해 MySQL를 사용합니다. 주로 MySQL을 이용해 비즈니스 로직을 처리하고, 비동기적인 처리를 위해 메시지를 Kafka에 publish 합니다.Kafka 도입 초기에는 아래 코드와 같이 메시지를 publish 했습니다. DB 트랜잭션이 완료된 이후 Kafka에 메시지를 publish 하고, 실패한 메시지를 dead_letter_queue DB 테이블에 저장한 뒤 별도의 batch process에서 retry를 하는 방식이었죠.그러나 코드 베이스에서 Kafka를 점점 더 많이 도입하게 되자, 아래와 같은 한계가 드러났습니다.• DB 트랜잭션과 메시지 publish를 원자적으로 처리할
kafka
mysql
relay
연관 기술 스택
techstack-logo
Apollo
techstack-logo
GraphQL
Copyright © 2025. Codenary All Rights Reserved.