[개발일지] 230620 GraphQL과 AppSync를 이용해 실시간 데이터 전송 (With Redis)

 

며칠 전 회사에서 단순 API 기능 개발을 하고 있던 나에게 실시간 관련 기능에 필요한 간단한 실시간 파이프 라인을 한번 구축해 보는 게 어떻겠냐고 제의를 해주셔서 아무것도 모르지만 일단은 해보겠다고 했다. (오랜만에 새로운 기술을 접한다는 사실에 설렜었다..)

 

그래서 약 일주일 좀 넘게 GraphQL, AppSync, Amplify, Quarkus 등등 새로운 기술을 많이 접하고 공부를 했다. 이해 안 되는 부분들(특히나 GraphQL, AppSync, Amplify 이 세 개의 전체적인 상관관계를 파악하는 게 힘들었다.)은 물어보면서 최대한 이해하려고 했다. 하지만 아직은 저것들을 누군가에게 설명할 수 있을 정도로 공부한 건 아니지만 기초적인 개념과 사용법 정도는 익힌 것 같다.

 

그래서 오늘은 지금까지 공부한것들을 바탕으로 파이프라인을 구축하기 위해 직접 로컬에 작업을 진행했다.

 

현재 상황


주식장의 개장 여부를 AppSync를 통해 실시간으로 데이터를 전송해야 하는 상황 (주식장의 개장 여부에 따라 화면에 보이는 값이 다르기 때문에 실시간성이 중요)

 

주식장의 오픈 여부에 대한 값을 갖고 있는 객체를 MarketStatus라고 하자.

 

MarketStatus에 대한 graphql 스키마는 정의해서 aws가 자동으로 mutation, subscription을 만들어 줬다.

resolver를 이용하면 더 복잡한 query나 mutation을 만들 수 있다고 하지만 자동으로 생성해 주는 crud대한 것들만 있어도 되기 때문에 resolver에 대해 딥하게는 알아보지 않았다.

 

서버에서 graphql 엔드포인트에 쿼리를 날리면 서버에서 작성한 쿼리를 바탕으로 데이터를 가져오거나 생성, 업데이트, 삭제를 한다. (db는 aws의 DynamoDB를 사용)

 

서버에서 graphql 쿼리를 통해 생성한 데이터를 프론트에서는 어떻게 받는지에 대해 내가 이해한 대략적인 흐름은 아래와 같다.

 

쿼리를 날리면 mutation이 발생해(여기서는 mutation을 실행하는 쿼리 작성) 결괏값이 정상적으로 반환이 되면 해당 mutation을 바라보고 있는(구독하고 있는) subscription이 실행되어 mutation 쿼리로 생성된 결괏값을 프론트로 전달한다.

 

구독 설정은 자동으로 설정되어 있다. 예를들어, onUpdateXXX 형태의 subscription은 updateXXX mutation을 구독하고 있다.

아래 예시 코드를 보면 이해하기 쉽다.

 

예시 코드

input UpdateMarketStatusInput {
	isMarketOpen: Boolean
}

type Mutation {
	updateMarketStatus(input: UpdateMarketStatusInput!): MarketStatus
}

type Subscription {
	onUpdateMarketStatus: MarketStatus @aws_subscribe(mutations: ["updateMarketStatus"])
}

 

문제 상황


marketStatus 값을 프론트로 전달을 하는데 주식장 오픈, 마감시간은 일반적으로 09:00에 오픈해서 15:30분에 닫는다. 그럼 이 시간에만 marketStatus 값을 변경해서 전달해 주면 되는 게 아닌가 생각할 수 있다.

하지만 주식장은 항상 09:00에 오픈하는것이 아니다. 주말과 공휴일에는 주식을 개장하지 않고, 1년에 1번 시행하는 수능날에는 주식장 오픈 시간이 1시간 미뤄져 10:00에 오픈하고 16:30에 마감한다. 그리고 새해에도 오픈시간이 10:00이다 하지만 마감시간은 15:30으로 그대로이다. 이처럼 주식 개장시간은 항상 고정값이 아니다.

 

그래서 주식 개장 여부에 대한 검사하는 로직이 필요했고, db에 저장되는 마지막으로 주식이 거래된 날짜와 redis에 저장되는 최근 영업일을 이용해 주식장 개장 여부에 대한 판단을 했다.

 

이제 주식장이 열렸는지 닫혔는지 실시간으로 판단해야 했기 때문에 이 marketStatus 값을 특정 주기마다 쿼리를 발생시켜 프론트에 값을 전달해야 했다.

 

스케줄러를 이용해 1분마다 메서드를 실행해 mutation query를 날려 MarketStatus의 값을 생성할 수 있었지만 이렇게 되면 생성되는 데이터가 1분에 1개로 하루면 1440개가 db에 쌓이게 된다. 너무 비효율적이다.

 

아니면 특정 시간에만 로직 검사해서 쿼리 날리는 방법도 있을 것 같은데 (장 오픈, 마감 시간 5분 전부터 5분 후까지 (ex 08:55 ~ 10:05)) 이전 방법보다는 데이터가 덜 생성되겠지만 어쨌든 db에 불필요하게 데이터가 쌓인다.

 

마지막으로 생각해낸 방법이 redis를 활용하는 것이다.

 

결론


MarketStatus 데이터 생성을 최소화하기 위한 방법 (redis 사용)

매일 아침 8시에 redis에 marketStatus 값(false)을 저장한다. false를 저장하는 이유는 아침 8시는 항상 장 마감이기 때문 false값 저장

@Scheduled를 이용해 1분마다 db의 최근 영업일과 redis의 최근 영업일을 가져와 비교해서 현재 marketStatus 값을 얻는다.

이후 현재 marketStatus값과 redis에 저장된 marketStatus을 비교해 다르면 redis의 marketStatus를 현재 marketStatus값으로 변경하고, mutation query를 실행한다. (CreateMarketStatus)

 

→ 결론적으로 marketStatus값이 변경될때만 db에 데이터(MarketStatus)가 생성되므로 가장 효율적이라고 생각했다.

 

내일 이 방법을 공유드리고 다른 좋은 아이디어가 없는지 이야기를 나눠봐야겠다.

 

사실 원래 생각했던건 true -> false 변경될 때, Create가 아닌 Update mutation Query를 날릴 수 없을까 생각했는데 update mutation 쿼리를 실행하기 위해서는 변경시킬 데이터의 id값 필요한데 MarketStatus를 생성할 때 id값을 따로 만들어주지 않아서 id값이 자동으로 생성이 된다.

그래서 AWS DynamoDB 테이블에가서 생성된 MarketStatus의 id값을 가져와서 쿼리 날릴 때 하드코딩으로 id값 넣어줬더니 에러발생하길래 일단은 Create로 했는데 내일 Update mutation query 쪽에 대해 파봐야겠다.