꼼삐에서 식물 검색을 할 때 실시간 인기 식물 순위가 노출되면 좋겠다는 요구사항이 있었다. 사실 복잡한 로직을 요하는 기능은 별로 개발해본 적이 없었어서, 요구사항을 듣고서 바로 구현방안이 떠오르지 않았다. 어떤 자료구조를 활용해서 어떻게 구현할 지 조금 고민하다가 금방 구현을 하긴 했다. 그런데 최근에 어떤 회사 채용 과정에서 관련 내용을 묻는 질문이 있었는데, 까먹어버려서 뭔가 뚜렷하게 답을 하지 못했다. 잊지 않도록 상세하게 구현 내용을 좀 적어두려고 한다.1. 기능 요구사항'인기 순위' 라는 기능을 개발하기 위해서는 생각보다 여러 조건들이 필요했다. 우선 '인기' 라는 것을 어떤 지표로 판단할 것인지를 정해야 했다. 직관적으로 떠올릴 수 있는 지표는 조회수, 관련 게시글 수(이 경우에는 관련 게시..
개발/기타
이전에 진행한 내잔고를 부탁해 프로젝트에서는 Redis 를 활용한 캐싱 처리를 제대로 사용하지 못한 느낌이 있었다. 그때는 어떤 데이터를 캐싱해야할지 잘 감이 안와서 그냥 남들 하는대로 Refresh 토큰을 저장해두는 정도로만 활용하였다. 대신 채팅 기능을 구현할 때 사용자의 구독 정보 관리와 서버간 메세지 브로커로 활용한 바 있다. 물론 대규모의 트래픽 처리를 하는 경우에는 메세지 브로커로 Kafka 를 쓰겠지만, 그래도 알차게 Redis 를 활용했다고 생각한다. 이번 프로젝트에서는 Redis 의 캐싱 기능을 더 야무지게 활용하여 api 응답 시간을 줄이고자 하였다. 사실 기술적으로 별 내용은 아닌데 나름 참고할만한 유즈케이스 같아서 기록해 둔다.1. 캐싱 데이터캐싱 처리는 데이터를 가지고 올때 DB ..
이전글 [https://techforme.tistory.com/48] 이전글에서 서버를 이중화 하면서 authorization_request_not_found 오류가 나는 상황에 대한 해결책으로 ip hash 방식의 부하분산을 이야기 했었다. 이는 간단한 해결책이긴 했지만, 만약 부하 분산 방식을 round robin 으로 바꾸라는 요구사항이 생긴다면, 전혀 대응할 수 없는 미봉책이었다. (사실 Nginx 를 더 활용해 보고 싶어서 해봤다.) 근본적인 해결책이 필요했다. 이번 게시글에서는 OAuth Authorization 요청 정보를 서버 내부에 저장하지 않고 외부에 저장함으로써, Nginx 의 부하 분산 알고리즘과 상관없이 해당 문제를 해결할 수 있는 방법을 설명한다.1. 문제 상황 분석1) 문제 발..
이전에 지리데이터를 다루면서 "클러스터링" 이라는 단어를 잘 모르고 쓴 적이 있다. 개발 초기, 창고의 검색 방식에 대해 고민하다가 "위치 정보 데이터가 많아지면 대체 원하는 정보를 어떻게 조회해오지?" 라는 거의 공상에 가까운 생각을 했다. 일단은 어떤 한 점(위경도)에서 주어진 반경 내의 데이터를 조회한다고 했을 때, 가장 먼저 떠올릴 수 있는 방법은 모든 데이터와의 거리를 일일이 다 계산해서 주어진 반경 내에 있는지 판단하는 방식을 먼저 떠올렸다. (그때는 풀 스캔, 시퀀셜 스캔이라는 단어도 몰라서 내가 알고 있는 단어인 브루트 포스 단어로 이 방법을 지칭했다.) 그러다가 당근 마켓을 떠올렸고 비슷한 지역끼리 묶어서 그 근처 지역만 먼저 조회하면 되지 않나? 라는 생각에서 "클러스터링" 이라는 말을..
이전 포스팅에서 무중단 배포를 rolling 방식, blue-green 방식으로 구현했었다. 그렇게 해놓고 보니, 서버가 이중화 되었을 때도 기능들이 제대로 동작하는지 테스트 하고 싶어졌다. 단일 서버였다가 서버 개수가 두 개 이상으로 늘어나면 각각의 요청이 동일한 서버가 아닌 다른 서버로 연결 되기 때문에 예기치 않은 문제들이 생길 수 있다. 특히 서버 내부의 메모리로 관리하는 자료가 있다면 문제가 발생하기 쉽다. 이 경우 서버의 메모리마다 관리하는 정보가 각각 달라지는데 만약 요청을 서로 다른 서버가 처리하게 되면 동일한 결과가 나오지 않게 되기 때문이다. stateful 한 서버가 되는 것이다. 예를 들어 로그인 정보를 각각의 서버에서 저장하고 있게 되면 로그인 정보를 가지고 있지 않은 서버에 요..
오늘은 너무 유명한 N+1 문제와 이와 관련한 EntityManager 의 getReference 라는 메서드를 이야기 하려고 한다. 사실 N+1 이 발생하는 원인이나 해결책은 간단한데, 조금 애매한 부분이 있어서 혼란을 겪었다. N+1 문제 N+1 문제는 자바 ORM 에서 객체 내부의 연관된 객체에 접근하려고 할때 발생한다. DB에서 객체를 로드해 올때 객체의 필드에 다른 엔티티가 포함되어 있는 경우, ORM 은 해당 엔티티의 정보를 전부 가지고 오지 않는다. (select query 를 생각해보면 당연하다. 연관된 다른 테이블의 자료는 조회해오지 않는다.) 그 대신 객체 행세(?)를 할 수 있는 프록시 객체(위임 객체)를 넣어주는데, 이 프록시 객체는 껍데기만 가지고 있을 뿐이고 변수에 데이터를 가지..
지난 글 마지막에 Enum Type 의 필드에 대해서는 1. Enum Type 은 일반적인 String 또는 Long, Double 같은 클래스와 다르게 별도의 애너테이션을 만들어야 한다. 2. 아예 엉뚱한 값이 들어오면 직접 구현한 Validator 가 작동하기 전에 Parsing 과정에서 Exception 이 터지기도 한다. 라고 이야기 했는데 이 두 문제를 해결하여 적절한 에러메세지를 반환해 보았다. 일단 2번 문제에 대해서 이야기하자면 Serialize 와 Deserialize 의 개념에 대해서 먼저 정리를 해야한다. (직렬화 / 역직렬화) 위 개념은 객체나 데이터를 더 낮은 수준의 데이터 형태로 전환 하거나 그 반대 과정을 말한다. 더 낮은 수준의 데이터라는 건 가만히 생각해보면 조금 모호한 개..
Pull Request 내용 하 글쓰다가 날라가서 쓸 맛이 안난다 Issue / PR 내용만 복붙하고 어떻게 구현 했는지 써야겠다. Issue 내용 요청에 대한 유효성 검사(Validation) 구현 #21 현재 Web 에서 API 로 향하는 요청에 대하여 Front-end 에서의 유효성 검사가 수행되고 있으나 Postman 등 다른 경로를 통해 들어온 요청에 대해서는 해당 유효성 검사를 거치지 않으므로 서버에서 별도의 Validation 을 수행하여 예기치 못한 오류가 발생하지 않도록 해야합니다. (예를 들어 특정 좌표 및 반경을 기준으로 창고를 조회하는 api 에서 반경 값을 아주 큰 수로 지정하게 되면 DB에 저장된 모든 Storage 를 서버로 가져오게 되므로 아주 큰 부하를 야기할 수 있습니다...
Spring Security 와 Jwt 현재 진행중인 프로젝트는 Spring Security 프레임워크를 토대로 하고 있으며, username/password 기반의 Form 로그인이 아닌 OAuth 2.0을 통해 로그인 처리를 수행한다. 또한, stateless 한 서버를 만들기 위해 Session 을 사용하지 않고 Jwt 를 발급/검증 하여 Authentication 을 하고 있다. 이는 단일 서버로 구동중인 본 프로젝트에서는 불필요한 방식이라고도 볼 수 있으나, 서비스가 커져서 여러개의 서버로 구성되는 상황을 염두에 둔 설계이다. Spring Security + OAuth2.0 + JWT 는 최근 프로젝트에서 꽤 정형화된 패턴의 보안 설계로 보인다. 그런데 그만큼 프레임워크가 많은 부분을 커버하고 ..
부끄러운 이야기지만 아직까지 프로젝트의 로그인 처리 로직이 완성되지 못했었다. 나도, 다른 팀원도 전부 로컬환경에서 작업을 하다가 이번에 DB를 바꾸면서 서버 환경에서 다시 로그인 및 회원가입 로직을 시험했었는데 로컬에서는 멀쩡하게 작동하는데 서버에서는 403 에러가 뜨는 것이었다. (앞으로는 잘 사진을 찍어서 남겨야겠다.) 이런 경우 EC2 에 접속해서 에러 로그를 찾아보아야 하나, 아직 에러로그를 어떻게 확인하는 지 잘 모른다. (이전에 Log4j2 설정을 공부하면서 만져보긴 했는데 도무지 로그파일이 어디에 저장되는지 찾아내는 데에 실패해서 약간 포기상태에서 머물고 있는 상태이다.) 그래서 수동으로 서버를 중지하고 다시 켜서 실시간으로 에러 메세지를 확인해보니, Token 이 만료되었다는 메세지를 확..