Blog
Anki
면접 질문 대비

ELK 스택에서 ELK가 각각 무슨 역할을 하나요?

답 보기
  • Elasticsearch
    • 데이터 저장소 및 검색/분석 엔진
  • Logstash
    • 데이터 수집 및 처리
    • 데이터를 가공(필터링, 변환, 형식화)하여 Elasticsearch에 전송
  • Kibana
    • 데이터 시각화 및 대시보드
    • Elasticsearch에 저장된 데이터를 시각화해서 사용자에게 제공

Prometheus, Grafana, Loki, Promtail 대해서 설명해주세요

답 보기
  • Prometheus

    • 모니터링 및 경고 시스템으로, 주로 시계열 데이터(시간에 따라 변하는 값)를 수집하고 저장하는 데 사용
    • Polling 방식으로 동작
      • Prometheus는 설정된 타겟(Endpoints)으로 주기적으로 요청을 보내 데이터를 가져옴
      • 장점 : Prometheus가 데이터를 제어하므로 장애 관리 용이.
      • 단점 : 타겟 엔드포인트가 반드시 데이터를 노출해야 함
  • Grafana

    • 데이터 시각화 및 대시보드 도구로, 다양한 데이터 소스에서 정보를 가져와 시각적으로 표현하는 역할
  • Loki

    • 로그 데이터 저장 및 검색 엔진
  • Promtail

    • Loki와 함께 사용되는 로그 수집기
    • Pushing 방식으로 동작
      • Promtail은 서버의 로그 파일을 읽고, 필터링 및 파싱한 후 데이터를 Loki로 전송
      • 장점: 로그 데이터를 실시간으로 전송 가능하며, Loki와의 통합에 최적화됨
      • 단점: Push 모델이므로 Promtail 설정 및 Loki 연동이 필요

템플릿엔진의 동작 방식을 생각해보고 ROR에서 MVC의 V를 Vue로 하는게 왜 말이 안되는건지 100자로 답해주세요.

꼬리 질문
  • 지금 당장 몰라도 됨

RoR에서 Vue를 사용하는 방법?

답 보기

템플릿 엔진은 서버에서 HTML을 생성해 클라이언트로 전달하지만, Vue는 클라이언트에서 DOM을 동적으로 렌더링합니다. ROR의 V는 서버 측 View를 의미하므로 Vue와 역할이 충돌합니다.

  • HTML (HyperText Markup Language)

    • HTML은 웹 페이지의 구조와 내용을 정의하는 마크업 언어입니다.
    • 웹 페이지의 텍스트, 이미지, 링크, 폼 등 다양한 요소를 배치하고 구성합니다.
    • 정적 문서입니다.
  • DOM (Document Object Model)

    • DOM은 HTML 문서를 프로그래밍적으로 조작할 수 있게 해주는 트리 구조의 객체 모델입니다.
    • 자바스크립트와 같은 프로그래밍 언어를 사용하여 웹 페이지의 구조, 스타일, 내용을 동적으로 변경할 수 있습니다.
    • 동적 문서 모델입니다.
  • 꼬리 질문 답

임베디드 템플릿(erb, ejs, php, ...)으로 마크업 짜놓고, 마크업 짜놓은거 기반으로 vuejs로 동적인 UI 제공하는게 의외로 해볼만한 트릭이에요.

Wait Free와 Lock Free에 대해서 설명해주세요

답 보기
  • 동시성 문제를 겪을 때 Lock을 사용하면 반드시 느려질 수 밖에 없음
    • 이 때 동시성 문제를 해결하면서도 좀 더 빠르게 동작하게 하기 위해서 사용하는게 Lock FreeWait Free
    • Lock을 사용하지 않고 CAS 연산을 통해서 동시성을 보장하는데, 이 때 Java에서는 Atomic을 활용해서 CAS 연산을 한다.
      • 예시 👇
AtomicInteger atomic = new AtomicInteger(0);
while(!atomic.compareAndSet(value, update)) {
  doSomethig();
}
  • Lock Free
    • 적어도 하나의 스레드가 진행됨을 보장
    • 일부 스레드는 무한정 대기할 수 있음
    • Wait Free보다 구현이 상대적으로 단순
    • 여러 스레드가 경쟁하지 않는 상황이라면 CAS 연산은 항상 성공하고, 여러 스레드가 경쟁을 한다고 해도 최소한 하나의 스레드는 반드시 성공하기 때문에 성공한 스레드는 작업을 진행되기 때문
  • Wait Free
    • 모든 스레드가 정해진 시간 내에 작업을 완료할 수 있음을 보장
    • 다른 스레드의 진행 상태와 관계없이 작업 완료 가능
  • Wait-Free는 Lock-Free의 상위 집합으로 볼 수 있음
    • 모든 Wait-Free 알고리즘은 Lock-Free이지만, 모든 Lock-Free 알고리즘이 Wait-Free인 것은 아님

Redis Sentinel과 Redis Cluster의 차이점은 무엇이고 장단점이 무엇일까요?

답 보기
  • Redis Sentinel

목적: 단일 마스터-복제본 구조에서 고가용성(High Availability) 제공

주요 특징:

  • 마스터 노드 장애 감지 및 자동 복제본 승격
  • 클라이언트 서비스 디스커버리 제공
  • 모니터링 및 알림 기능
  • 데이터 샤딩 없음 - 모든 데이터가 마스터 한 곳에 저장

장점 ✅:

  • 설정과 관리가 간단함
  • 자동 장애 조치로 다운타임 최소화
  • 기존 단일 Redis 인스턴스에서 쉽게 마이그레이션 가능
  • 클라이언트 투명성 (failover 시 자동 재연결)

단점 ❌:

  • 단일 마스터 병목: 모든 쓰기가 하나의 마스터로 집중
  • 수평 확장 불가능 - 데이터가 한 노드 메모리 용량에 제한
  • 대용량 데이터셋 처리 한계
  • 높은 처리량(high throughput) 시나리오에 부적합

  • Redis Cluster

목적: 수평 확장(Horizontal Scaling)과 데이터 분산을 통한 성능 향상

주요 특징:

  • 자동 데이터 샤딩 (16,384개 해시 슬롯 사용)
  • 다중 마스터 노드로 쓰기 분산
  • 각 마스터마다 복제본 운영으로 고가용성도 제공
  • 온라인 리샤딩 지원

장점 ✅:

  • 무제한 수평 확장: 노드 추가로 용량과 처리량 증가
  • 대용량 데이터셋 처리 가능
  • 높은 처리량과 성능
  • 단일 실패 지점(SPOF) 없음
  • 온라인으로 노드 추가/제거 가능

단점 ❌:

  • 설정과 관리가 복잡함
  • 트랜잭션이 여러 노드에 걸치면 제한적
  • 클라이언트에서 클러스터 인식 로직 필요
  • 작은 데이터셋에는 오버헤드

  • 언제 사용해야 할까?

Redis Sentinel을 선택하는 경우:

  • 📊 중소규모 애플리케이션에서 고가용성이 필요할 때
  • 🔒 단일 노드 메모리 용량으로 충분한 데이터량
  • 💾 캐싱 용도로 주로 사용할 때
  • 🛠️ 단순한 설정과 관리를 원할 때
  • 🔄 기존 단일 Redis에서 점진적 마이그레이션이 필요할 때
# 적합한 시나리오 예시
-  애플리케이션 세션 저장소
- 소규모 캐싱 시스템
- 실시간 리더보드 (데이터량이 적은 경우)

Redis Cluster를 선택하는 경우:

  • 🚀 대용량 데이터셋을 처리해야 할 때
  • ⚡ **높은 처리량(high throughput)**이 필요할 때
  • 📈 수평 확장이 필요한 성장하는 서비스
  • 🌐 글로벌 서비스에서 지역별 데이터 분산이 필요할 때
  • 💪 고성능과 가용성을 모두 요구할 때
# 적합한 시나리오 예시
- 대규모 전자상거래 상품 카탈로그
- 실시간 분석 데이터 처리
- IoT 데이터 수집  처리
- 대규모 게임 서버 데이터

  • 요약 비교표
구분Redis SentinelRedis Cluster
주목적고가용성수평 확장 + 고가용성
데이터 분산❌ (단일 마스터)✅ (자동 샤딩)
확장성수직 확장만수평 확장
설정 복잡도간단복잡
적합한 규모중소규모대규모
클라이언트 지원간단클러스터 인식 필요

Redis Cluster에서 Split Brain 현상에 대해서 설명해보세요.

답 보기
  • Split Brain 현상

네트워크 파티션으로 클러스터가 분할되어 여러 개의 독립적인 제어 중심이 동시에 존재하면서 데이터 일관성이 깨지는 현상입니다.

  • Redis Cluster 동작 원리

  • 16,384개 해시 슬롯을 마스터 노드들이 분할 관리

  • 쿼럼 기반 의사결정: 과반수 마스터가 통신 가능해야 정상 동작

  • Gossip 프로토콜로 노드 상태와 슬롯 정보 실시간 동기화

  • Configuration Epoch로 클러스터 설정 버전 관리

  • Split Brain 발생 시나리오

  1. 짝수 개 노드의 2분할 (최악의 경우)
  • 6개 마스터 → 3:3 분할
  • 양쪽 모두 과반수 미달이지만 각자 "정당한 클러스터"로 착각
  • 동일한 키에 서로 다른 값 저장 → 데이터 일관성 파괴
  1. 홀수 개 노드의 3분할
  • 7개 마스터 → 3:2:2 분할

  • 모든 그룹이 과반수 미달 → 전체 클러스터 쓰기 불가

  • Redis Cluster의 방지 메커니즘

  1. 쿼럼 기반 상태 관리
  • 과반수 미만 시 자동으로 읽기 전용 모드 전환
  • CLUSTERDOWN 상태로 쓰기 작업 거부
  1. Write Safety 보장
  • 비동기 복제로 인한 데이터 손실 가능성 인정
  • Last Failover Wins 정책으로 최종 선출된 마스터 데이터 우선
  1. Replica Election 과정
  • 마스터 장애 시 과반수 마스터의 동의로만 복제본 승격

  • Configuration Epoch 증가로 새로운 클러스터 설정 전파

  • 방지를 위한 모범 사례

  1. 노드 구성
  • 홀수 개 마스터 (최소 3개, 권장 5~7개)
  • 마스터당 최소 1개 복제본 구성
  1. 지리적 분산
  • 주 데이터센터에 과반수 노드 배치
  • 부 데이터센터들에 나머지 노드 분산
  1. 타임아웃 설정
  • cluster-node-timeout: 15초 (기본값)

  • cluster-replica-validity-factor: 10

  • CAP 정리 관점

Redis Cluster는 CP(Consistency + Partition Tolerance) 선택:

  • 일관성 우선: 과반수 미달 시 가용성 희생

  • 분산 허용: 파티션 감지 후 안전 모드 전환

  • 실무 대응 방안

  1. 모니터링
  • CLUSTER NODES, CLUSTER INFO로 상태 감시
  • Node TimeoutFail State 알림 설정
  1. 애플리케이션 레벨
  • CLUSTERDOWN 예외 처리
  • Fallback 저장소 (DB, 다른 캐시) 준비
  1. 복구 절차
  • 네트워크 복구 후 데이터 일관성 검증

  • 필요시 수동 슬롯 재할당복제본 재동기화

  • 한계점과 대안

  1. Redis Cluster 한계
  • 복잡한 파티션에서는 완전한 해결 불가
  • 수동 개입 필요한 상황 발생 가능
  1. 대안적 해결책
  • Redis Sentinel: 단순한 HA 구조에서 더 나은 가용성
  • Strong Consistency 시스템: Zookeeper, etcd 활용
  • 애플리케이션 샤딩: 클라이언트 레벨 데이터 분산 제어

출처: Redis Cluster Specification (opens in a new tab)

실제 프로덕션 환경에서 회원 테이블의 검색 성능을 개선하려고 합니다. 어떤 컬럼에 인덱스를 생성하는 것이 효과적일까요? 카디널리티 관점에서 설명해주세요.

꼬리 질문
  • 복합 인덱스를 생성한다면 컬럼 순서는 어떻게 정하시겠습니까?
  • 카디널리티가 낮은 컬럼에 인덱스를 생성하면 어떤 문제가 발생하나요?
  • 인덱스 생성 후 성능이 오히려 저하되는 경우는 언제인가요?

답변 보기
  • 카디널리티가 높은 컬럼부터 우선적으로 인덱스를 생성해야 합니다 📈
  • 회원 테이블 기준 카디널리티 순서:
    • 높음: email, user_id, phone_number (거의 유니크)
    • 중간: birth_date, join_date, last_login_date
    • 낮음: gender, user_status, user_grade (값의 종류가 적음)
  • WHERE 절에 자주 사용되는 컬럼 중 카디널리티가 높은 것을 선택
    • 예: WHERE email = 'user@example.com' → email 인덱스 생성
    • 예: WHERE user_status = 'active' → 비효율적 (전체 회원의 90%가 active일 수 있음)
  • 꼬리질문: 복합 인덱스를 생성한다면 컬럼 순서는 어떻게 정하시겠습니까?
    • 카디널리티가 높은 컬럼을 앞에 배치하는 것이 일반적 ✅
    • 예: (email, user_status) > (user_status, email)
    • 단, 쿼리 패턴에 따라 달라질 수 있음
      • WHERE user_status = 'active' AND created_at > '2024-01-01' 같은 쿼리가 많다면
      • (user_status, created_at) 인덱스가 더 효율적일 수 있음
    • = 조건이 > < 조건보다 앞에 오는 것이 좋음
  • 꼬리질문: 카디널리티가 낮은 컬럼에 인덱스를 생성하면 어떤 문제가 발생하나요?
    • 인덱스 스캔이 테이블 풀 스캔보다 느려질 수 있음 🐌
    • 예시: gender 컬럼 (M/F 두 값만 존재)
      • 인덱스로 50%만 걸러냄 → 나머지 50%는 랜덤 액세스 발생
      • 차라리 풀 스캔이 순차 I/O라 더 빠를 수 있음
    • 인덱스 저장 공간만 낭비하고 성능 개선 효과는 미미
    • INSERT/UPDATE 시 인덱스 업데이트 오버헤드만 증가
  • 꼬리질문: 인덱스 생성 후 성능이 오히려 저하되는 경우는 언제인가요?
    • 쓰기 작업이 많은 테이블의 경우 ⚠️
      • 인덱스가 많을수록 INSERT/UPDATE/DELETE 시 모든 인덱스 업데이트 필요
      • 예: 로그 테이블, 실시간 채팅 메시지 테이블
    • 작은 테이블의 경우 (수백 건 이하)
      • 인덱스 탐색보다 풀 스캔이 더 빠름
    • 인덱스 선택도(Selectivity)가 나쁜 경우
      • 조회 결과가 전체 데이터의 15-20% 이상이면 풀 스캔이 유리
    • 복합 인덱스의 첫 번째 컬럼을 사용하지 않는 쿼리

게시판 서비스를 운영 중인데, 메인 페이지는 5초가 걸리는데 상세 페이지는 0.1초 만에 로딩됩니다. 어떤 문제가 있을 수 있고, 어떻게 해결하시겠습니까?

꼬리 질문
  • N+1 문제를 해결하는 구체적인 쿼리를 작성해보세요
  • 페이지네이션 구현 시 OFFSET vs CURSOR 방식의 장단점은?
  • 캐싱을 적용한다면 어떤 전략을 사용하시겠습니까?

답변 보기
  • 메인 페이지와 상세 페이지의 속도 차이는 주로 N+1 쿼리 문제 때문입니다 🔍
  • 문제 진단:
    • 메인: 게시글 목록 조회(1) + 각 게시글의 댓글 수 조회(N) = N+1번 쿼리
    • 상세: 단일 게시글 조회(1) + 해당 댓글 조회(1) = 2번 쿼리
  • 추가 가능한 원인들:
    • 페이지네이션에서 OFFSET 사용으로 인한 성능 저하
    • 인덱스 미사용 또는 잘못된 인덱스 설계
    • 불필요한 컬럼까지 모두 SELECT하는 경우
    • 복잡한 서브쿼리나 여러 테이블 JOIN
  • 꼬리질문: N+1 문제를 해결하는 구체적인 쿼리를 작성해보세요
    • 문제가 되는 쿼리:
    -- 1. 게시글 목록 조회
    SELECT * FROM posts ORDER BY created_at DESC LIMIT 20;
     
    -- 2. 각 게시글마다 댓글 수 조회 (20번 실행)
    SELECT COUNT(*) FROM comments WHERE post_id = ?;
    • 해결 방법 1 - JOIN 사용:
    SELECT p.*, COUNT(c.id) as comment_count
    FROM posts p
    LEFT JOIN comments c ON p.id = c.post_id
    GROUP BY p.id
    ORDER BY p.created_at DESC
    LIMIT 20;
    • 해결 방법 2 - 서브쿼리 사용:
    SELECT p.*,
           (SELECT COUNT(*) FROM comments c WHERE c.post_id = p.id) as comment_count
    FROM posts p
    ORDER BY p.created_at DESC
    LIMIT 20;
  • 꼬리질문: 페이지네이션 구현 시 OFFSET vs CURSOR 방식의 장단점은?
    • OFFSET 방식:
      • 장점: 구현이 간단, 특정 페이지로 바로 이동 가능 📄
      • 단점: 페이지가 뒤로 갈수록 성능 저하 (앞의 모든 데이터 스캔)
      • 예: LIMIT 20 OFFSET 10000 → 10,020개 조회 후 20개만 반환
    • CURSOR(키셋) 방식:
      • 장점: 일정한 성능 보장, 대용량 데이터에 적합 🚀
      • 단점: 특정 페이지 점프 불가, 구현 복잡도 증가
      • 예: WHERE id < ? ORDER BY id DESC LIMIT 20
    • 추천: 무한 스크롤은 CURSOR, 페이지 번호가 필요하면 OFFSET
  • 꼬리질문: 캐싱을 적용한다면 어떤 전략을 사용하시겠습니까?
    • Redis를 활용한 페이지 단위 캐싱 💾
      • 키: main_page:1, 값: 직렬화된 게시글 목록
      • TTL: 1-5분 (실시간성과 성능의 균형)
    • 캐시 무효화 전략:
      • 새 게시글 작성 시: 첫 페이지 캐시만 삭제
      • 댓글 작성 시: 해당 게시글이 포함된 페이지 캐시 삭제
      • 또는 짧은 TTL로 자동 갱신
    • 캐시 워밍(Cache Warming):
      • 첫 1-3페이지는 미리 캐싱
      • 트래픽이 적은 시간에 주기적으로 갱신
    • 부분 캐싱 전략:
      • 댓글 수만 별도 캐싱: comment_count:post_id
      • 조회수는 별도 캐싱하여 실시간성 확보

일일 방문자 100만명인 뉴스 사이트에서 조회수를 실시간으로 업데이트하고 있습니다. DB 부하가 심한데 어떻게 개선하시겠습니까?

꼬리 질문
  • Redis와 DB 간 동기화는 어떻게 처리하시겠습니까?
  • 캐시 미스(Cache Miss)가 발생했을 때 대응 방안은?
  • 정확도와 성능 사이의 트레이드오프를 어떻게 조절하시겠습니까?

답변 보기
  • 매 요청마다 DB UPDATE는 비현실적이므로 메모리 기반 캐싱이 필수입니다 💡
  • 즉시 적용 가능한 해결책:
    • Redis를 활용한 조회수 캐싱
      • 키: view_count:article_id
      • 값: 현재 조회수
      • 조회 시: Redis INCR 명령어로 원자적 증가
    • 주기적 DB 동기화 (예: 5분마다)
    • 예상 부하 감소: 100만 요청 → 288회 업데이트 (하루 기준)
  • 구현 예시:
    # 조회수 증가
    redis.incr(f"view_count:{article_id}")
     
    # 배치로 DB 동기화 (5분마다)
    for key in redis.scan_iter("view_count:*"):
        article_id = key.split(":")[1]
        count = redis.getdel(key)  # 가져오고 삭제
        db.execute("UPDATE articles SET view_count = view_count + ? WHERE id = ?", [count, article_id])
  • 꼬리질문: Redis와 DB 간 동기화는 어떻게 처리하시겠습니까?
    • 동기화 전략 1 - 주기적 배치 처리 ⏰
      • 크론잡으로 5-10분마다 실행
      • Redis의 조회수 증분을 DB에 일괄 업데이트
      • 장점: 구현 간단, DB 부하 최소화
      • 단점: 최대 동기화 주기만큼의 데이터 손실 가능성
    • 동기화 전략 2 - 이벤트 기반 처리
      • 조회수가 특정 단위(100, 1000)에 도달할 때마다 동기화
      • 메시지 큐(Kafka, RabbitMQ)를 통한 비동기 처리
    • 동기화 전략 3 - Write-Behind 캐싱
      • 변경사항을 큐에 저장하고 배치로 처리
      • 실패 시 재시도 로직 포함
    • 데이터 정합성 보장:
      • Redis 스냅샷과 AOF 활성화
      • 동기화 전 Redis 값을 임시 백업
      • 트랜잭션으로 원자성 보장
  • 꼬리질문: 캐시 미스(Cache Miss)가 발생했을 때 대응 방안은?
    • Cache-Aside 패턴 적용 🔄
      def get_view_count(article_id):
          # 1. 캐시 확인
          count = redis.get(f"view_count:{article_id}")
          if count is not None:
              return int(count)
          
          # 2. DB에서 조회
          count = db.query("SELECT view_count FROM articles WHERE id = ?", [article_id])
          
          # 3. 캐시에 저장
          redis.set(f"view_count:{article_id}", count, ex=3600)
          return count
    • 캐시 스탬피드(Stampede) 방지:
      • 분산 락 사용: 첫 요청만 DB 조회, 나머지는 대기
      • 확률적 조기 만료: TTL 전에 미리 갱신
    • 페일오버 전략:
      • Redis 클러스터 구성으로 가용성 확보
      • 장애 시 DB 직접 조회 (Circuit Breaker 패턴)
  • 꼬리질문: 정확도와 성능 사이의 트레이드오프를 어떻게 조절하시겠습니까?
    • 서비스 특성에 따른 전략 선택 📊
      • 뉴스/블로그: 대략적인 조회수 OK → 10-30분 동기화
      • 실시간 랭킹: 정확도 중요 → 1-5분 동기화
      • 유료 컨텐츠: 매우 정확해야 함 → 즉시 동기화
    • 하이브리드 접근:
      • 일반 조회: Redis에서만 증가 (빠름)
      • 중요 임계값(1만, 10만): 즉시 DB 동기화
      • 표시는 근사치 사용: "조회수 1.2만" (정확한 숫자 불필요)
    • 모니터링 지표:
      • Redis-DB 간 차이율 추적
      • 동기화 지연 시간 측정
      • 사용자 만족도 vs 시스템 부하 균형점 찾기