#7 Server (3)
Q. Java 17 버전을 사용한 이유는 무엇인가?
A. Java 17은 LTS(Long-Term Support) 버전으로, 안정성과 장기적인 지원을 제공합니다. 또한, 성능 향상과 새로운 기능들, 특히 텍스트 블록을 통한 코드 가독성을 향상시킬 수 있었으며, record 객체를 이용하여 보일러플레이트 코드를 크게 줄일 수 있습니다.
Q. GraphQL과 REST의 차이는 무엇인가?
A. GraphQL은 클라이언트가 필요한 데이터만 요청할 수 있게 해주어 오버페칭과 언더페칭을 줄일 수 있으며, REST는 정형화된 HTTP 메서드를 사용해 리소스를 처리합니다. GraphQL은 하나의 엔드포인트로 다양한 쿼리를 처리할 수 있지만, REST는 리소스마다 별도의 엔드포인트를 가집니다.
Q. API Version Control이 필요한 이유는 무엇인가?
A. API가 업데이트될 때 기존 클라이언트가 영향을 받지 않도록 하기 위해서입니다. 버전 관리 없이 API가 변경되면 이전 버전과 호환되지 않아 클라이언트가 오류를 일으킬 수 있습니다. 이를 방지하고 호환성을 유지하기 위해 API 버전 관리는 필수적입니다.
Q. 욕설 필터링을 어떤 알고리즘으로 구현할 것인가?
A. KMP 알고리즘은 패턴 검색에 효율적이지만, 다수의 패턴을 동시에 검색하는 아호코라식 알고리즘이 욕설 필터링에 더 적합합니다. 아호코라식은 여러 패턴을 트라이와 유사한 자료구조로 구성해 빠르게 탐색할 수 있습니다. 따라서, Interceptor를 이용한 아호코라식 알고리즘을 활용해서 욕설 필터링을 구현할 것입니다.
Q. Lambda@Edge를 사용할 상황 예시를 들어보시오.
A. Lambda@Edge는 콘텐츠 전송 네트워크(CDN)에서 실행할 수 있는 서버리스 컴퓨팅 환경입니다. 예를 들어, 실시간으로 이미지 Resizing을 제공하거나, 쿠키에 따라서 다른 웹사이트로 리다이렉트하여 A/B 테스트 하는 용도로도 사용할 수 있습니다.
Q. 커서 기반 무한 스크롤 페이지네이션을 구현했는데, 게시글 총 수를 측정하는 Query를 매번 받아오다보니 시간이 더 걸렸습니다. 어떻게 개선할 수 있을까요?
A. 무한 스크롤에서 전체 게시물 수를 계산하는 count 쿼리가 성능에 영향을 미칠 수 있습니다. 특히, 대용량 데이터베이스에서 인덱스 최적화가 제대로 되어 있지 않다면 더 많은 시간이 걸립니다. 해결 방법은 캐싱된 카운트 데이터나 이벤트를 통해 비동기적으로 카운트를 관리하는 것입니다.
Q. 테스트 코드 커버리지의 목표는 어느 정도로 설정하는 게 좋을까?
A. 일반적으로 80% 이상의 커버리지를 목표로 하지만, 숫자에만 집중하지 않는 것이 중요합니다. 중요한 비즈니스 로직과 에러 핸들링 부분은 100%에 가깝게 커버되도록 노력하고, 테스트의 품질과 신뢰성을 우선시해야 합니다.
Q. Kotlin으로 개발할 때 CI/CD 시 코드 포매팅이나 안정성 기여를 어떻게 하는 것이 좋을까?
A. ktlint같은 도구를 사용하여 코드 스타일과 린팅을 자동화하고, Sonarlint 및 SonarQube같은 정적 코드 분석 도구 이용하여 CI/CD시 코드 분석을 하도록하여 코드 품질을 지속적으로 모니터링하고 유지할 수 있습니다.
Q. Java에서 ThreadLocal이란 무엇이며, 이를 사용할 때의 장단점은 무엇인가?
A. ThreadLocal은 각 스레드에 독립적인 변수를 저장할 수 있게 해주는 Java 클래스입니다. 즉, 동일한 변수를 여러 스레드가 공유하더라도 각 스레드마다 별도의 값을 유지합니다. 이를 통해 멀티스레드 환경에서 스레드 간의 데이터 충돌을 피하고, 안전하게 값을 저장할 수 있습니다.
Q. Redis와 같은 글로벌 캐시 서버에 장애가 발생할 경우 어떻게 대응할 수 있을까요?
A. Redis와 같은 글로벌 캐시 서버에 장애가 발생하면, 서비스가 500 Internal Server Error를 발생시킬 수 있습니다. 이를 방지하기 위해 로컬 캐시를 추가하여, 글로벌 캐시 장애 시에도 데이터를 빠르게 제공할 수 있습니다. 로컬 캐시는 데이터베이스 접근 빈도를 줄여 부하를 완화하고 응답 속도를 개선합니다. 또한, 서킷 브레이커 패턴을 적용해 장애 상황에서 시스템의 안정성을 높일 수 있습니다. 글로벌 캐시가 일정 시간 내에 응답하지 않으면, 서킷 브레이커는 바로 로컬 캐시나 데이터베이스로 전환해 더 이상의 요청을 차단합니다. 이를 통해 불필요한 재시도를 줄이고, 서비스 가용성을 유지할 수 있습니다.
Q. 로컬 캐시와 글로벌 캐시를 동시에 사용할 때 발생할 수 있는 문제는 무엇인가요?
A. 로컬 캐시와 글로벌 캐시를 동시에 사용할 때 가장 큰 문제는 데이터 동기화입니다. 각 서버에 존재하는 로컬 캐시와 중앙화된 글로벌 캐시의 데이터가 일치하지 않을 경우, 캐시된 데이터의 정합성이 깨질 수 있습니다. 이를 해결하기 위해 데이터 업데이트 주기를 기준으로 동기화하는 방법이 있습니다. 예를 들어, 주기적으로 글로벌 캐시와 로컬 캐시의 데이터를 비교해 동기화 상태를 점검하는 것입니다. 또한, Spring의 @Scheduled 어노테이션을 사용해 주기적으로 로컬 캐시를 초기화하거나 갱신하는 방법도 유효합니다. 이를 통해 동기화 문제를 해결하고, 로컬 캐시가 글로벌 캐시의 상태를 빠르게 반영할 수 있도록 할 수 있습니다
Q. 캐싱 전략 중 Look Aside와 Write Around 전략을 어떻게 활용했는지 설명해 주세요.
A. Look Aside 캐시는 먼저 캐시를 조회하고, 데이터가 없으면 데이터베이스에서 조회한 후 캐시에 저장하는 방식입니다. Write Around 캐시는 데이터를 캐시에 바로 쓰지 않고, 데이터베이스에만 쓰는 방식으로, 데이터 업데이트 시 캐시를 갱신하지 않아 쓰기 부하를 줄이는 장점이 있습니다.
Q. 추천 수에 따른 인기 순위로 글을 페이지네이션하여 보여주고자 합니다. 그런데 매번 사용자가 추천한 글 목록에서 join하여 count하는 것은 부하가 심하고, 효율적인 방법이 아닙니다. 어떻게 개선할 수 있을까요?
A. 추천 수를 별도의 칼럼으로 관리하여, 추천 시마다 증가시키고 이에 따라서 정렬하여 페이지네이션하면 됩니다.
Q. 추천 수와 같은 데이터에서 Redis 분산락을 사용하여 Race Condition을 예방했습니다. 하지만, 추천 수 같은 데이터는 동시에 트래픽이 몰리는 경우가 적어 Race Condition이 발생할 가능성은 적지만, 발생할 경우 치명적이기 때문에 예방해야 됩니다. 하지만 Redis 분산락을 거는 것은 리소스 낭비입니다. 어떻게 하는게 좋을까요?
A. 모든 Race Condition에 대해 Redis 분산락을 사용하는 것은 과도할 수 있습니다. 예를 들어, 추천수 증가와 같이 Race Condition이 발생할 확률은 낮지만 트래픽이 집중되지 않는 데이터에 대해 무조건 분산락을 걸면 성능에 영향을 미칠 수 있습니다. 이런 경우, 낙관적 락(Optimistic Lock)을 사용하는 것이 더 적합합니다. 낙관적 락은 데이터에 변경이 있을 때마다 버전을 체크하여 충돌을 감지하는 방식으로, 낮은 부하로 충돌을 관리할 수 있습니다. 이렇게 하면 필요 이상으로 락을 사용하지 않아도 되기 때문에 성능을 유지할 수 있습니다.
Q. 세션 쿠키 설정에서 same-site, secure, http-only를 사용하는 이유는 무엇인가요?
A. 세션 쿠키 설정에서 same-site, secure, http-only와 같은 옵션을 사용하는 이유는 보안 강화를 위해서입니다. same-site 옵션을 통해 쿠키가 사이트 간 요청에 포함될 수 있는지를 제한할 수 있으며, 이를 통해 CSRF 공격을 예방할 수 있습니다. 예를 들어, lax로 설정하면 사용자가 링크를 클릭했을 때만 쿠키가 전송됩니다. secure 옵션은 HTTPS를 통해서만 쿠키가 전송되도록 하여, 중간자 공격을 방지합니다. http-only 옵션은 클라이언트 측에서 JavaScript를 통해 쿠키에 접근하는 것을 막아 XSS 공격을 예방하는 데 도움이 됩니다.
Q. 이메일 인증을 구현할 때 고려해야 할 사항과 사용하는 기술은 무엇인가요?
A. 이메일 인증을 구현할 때는 보안성과 효율성을 고려해야 합니다. 유저가 회원가입 시 고유한 UUID를 생성하여, 이를 Redis에 저장하고 TTL(Time-To-Live)을 24시간으로 설정해 만료 시간을 지정합니다. AWS SES와 같은 외부 이메일 서비스로 인증 링크를 포함한 이메일을 전송하며, 이때 HTTPS를 사용해 보안을 강화해야 합니다. 유저가 링크를 클릭하면 UUID를 통해 인증을 검증하고, 유효하지 않은 경우에는 접근을 차단합니다. AWS SES는 이메일 전송의 안정성과 확장성을 제공하여 이메일 인증 작업을 효율적으로 처리할 수 있습니다.
Q. 대량 트래픽 환경에서 API 서버의 성능을 최적화하기 위해 어떤 방법을 사용할 수 있을까요?
A. 대량 트래픽 환경에서 API 서버의 성능을 최적화하려면 로드 밸런서를 사용해 트래픽을 여러 서버에 분산하고, 캐싱을 통해 자주 조회되는 데이터를 빠르게 반환할 수 있도록 해야 합니다. 또한, DB 쿼리 최적화와 인덱스를 사용해 데이터베이스 성능을 개선하고, 비동기 처리와 멀티스레딩을 활용해 요청을 비동기적으로 처리하여 응답 속도를 높일 수 있습니다. 서버 측에서는 Nginx, HAProxy와 같은 Reverse Proxy를 사용해 트래픽을 효율적으로 관리하는 것도 좋은 방법입니다.
Q. 트래픽 폭주 시 서버 과부하를 방지하기 위한 방법은 무엇인가요?
A. 트래픽 폭주 시 서버 과부하를 방지하기 위해 Rate Limiting을 적용해 특정 시간 내의 요청 수를 제한할 수 있습니다. 또한, 트래픽이 갑자기 증가할 때 Auto Scaling을 통해 서버 인스턴스를 자동으로 확장하고, Queue 시스템(SQS, Kafka 등)을 도입해 비동기 처리로 요청을 처리할 수 있습니다. 서버가 과부하에 빠지기 전에 Circuit Breaker 패턴을 도입해 오류가 발생한 외부 서비스와의 통신을 차단하는 것도 효과적입니다.
Q. 대규모 트래픽 환경에서의 데이터베이스 최적화 방법은 무엇인가요?
A. 대규모 트래픽 환경에서는 인덱스 최적화와 쿼리 튜닝이 필수적입니다. 자주 사용되는 쿼리의 컬럼에 적절한 인덱스를 추가해 검색 속도를 높이고, 불필요한 데이터를 읽지 않도록 쿼리를 최적화합니다. 또한, **샤딩(Sharding)**을 통해 데이터를 여러 서버에 분산해 성능을 높이며, 리플리케이션(Replication)을 통해 읽기 트래픽을 분산할 수 있습니다. 캐시를 사용해 자주 조회되는 데이터를 데이터베이스에서 직접 가져오지 않도록 하여 데이터베이스 부하를 줄일 수 있습니다.
Q. ORDER BY date_format(modified_date, "%Y-%m-%d") DESC, rank ASC가 포함된 쿼리에 modified_date와 rank로 인덱스를 설정했지만, 인덱스가 적용되지 않았습니다. 그 이유는 무엇일까요?
A. datetime 데이터를 가공하여 일별로 정렬하면 인덱스가 적용되지 않기 때문에 성능이 저하됩니다. 이를 해결하기 위해 컬럼 가공 없이 인덱스를 활용할 수 있도록, datetime 그대로 비교하거나 필요한 범위 조회를 통해 데이터를 정렬합니다.
Q. name 컬럼에 인덱스를 걸었을 때, 다음 세 가지 쿼리 중 인덱스를 효과적으로 활용하는 쿼리와 그렇지 않은 쿼리를 구분하고, 인덱스를 활용하지 못하는 경우 그 이유를 설명해보세요.
SELECT * FROM table_name WHERE name LIKE 'A%';
SELECT * FROM table_name WHERE name LIKE '%A';
SELECT * FROM table_name WHERE name LIKE '%A%';
A. 첫 번째 쿼리 LIKE 'A%'는 인덱스를 효과적으로 활용합니다. 이는 문자열이 'A'로 시작하는 값을 찾기 때문에, 인덱스의 B-트리 구조에서 정렬된 순서를 그대로 사용할 수 있기 때문입니다. 그러나 두 번째 쿼리 LIKE '%A'와 세 번째 쿼리 LIKE '%A%'는 인덱스를 활용하지 못합니다. 이는 문자열의 중간이나 끝 부분에서 패턴을 찾으므로, B-트리의 정렬된 구조와 검색 패턴이 일치하지 않아 테이블의 모든 레코드를 스캔해야 하기 때문입니다.
Q. 단위 테스트와 통합 테스트의 차이는 무엇이며, 어떤 상황에서 각각을 사용하는 것이 좋을까요?
A. 단위 테스트는 개별 메서드나 클래스의 동작을 검증하며, 통합 테스트는 여러 구성 요소가 함께 작동하는지를 확인합니다. 복잡한 비즈니스 로직이나 DB 통계를 검증할 때는 통합 테스트가, 메서드 호출 여부나 특정 로직의 간단한 검증은 단위 테스트가 적합합니다.
Q. 테스트 코드에서 자주 사용하는 mocking이란 무엇이며, 언제 사용하는 것이 적절한가요?
A. Mocking은 외부 의존성을 대체하는 가짜 객체를 생성하여 특정 상황을 시뮬레이션하는 것입니다. 외부 API, DB 연결 등의 외부 리소스에 의존하지 않고 메서드 호출이나 특정 로직을 검증할 때 유용합니다.
Q. 웹 사이트에서 크롤링 봇의 접근을 막기 위한 방법은 어떤 것들이 있으며, 각 방법의 장단점을 설명하시오.
A. 일반적인 방법으로는 robots.txt 파일 설정, IP 차단, User-Agent 필터링, CAPTCHA 사용, 요청 속도 제한(Rate Limiting), 비정상적 트래픽 탐지를 통한 Bot 차단 등이 있습니다. robots.txt는 친화적인 봇을 막을 수 있지만 악성 봇에는 효과가 없고, CAPTCHA는 인간과 봇을 효과적으로 구분하지만 사용자 경험에 부정적 영향을 미칠 수 있습니다. Rate Limiting은 API 남용을 방지할 수 있지만, 설정을 잘못하면 정당한 사용자도 차단될 위험이 있습니다.
Q. 외부 API 호출 시, 트랜잭션과 I/O 작업을 분리해야 하는 이유는 무엇이며, 이를 구현하기 위한 방법은 어떤 것이 있나요?
A. 트랜잭션을 길게 유지하면 커넥션 풀을 점유해 성능 저하를 초래할 수 있으므로, I/O 작업과 분리해 커넥션을 효율적으로 사용하는 것이 중요합니다. 이를 위해 퍼사드 패턴을 활용해 API 호출과 데이터 저장을 분리하거나, 이벤트를 사용해 트랜잭션이 종료된 후 비동기 작업을 처리하는 방식이 효과적입니다.
Q. 외부 API 요청 시 스레드풀을 사용하는 이유는 무엇이며, 올바른 스레드풀 크기를 결정하는 방법을 설명하시오.
A. 별도로 스레드 풀을 설정하지 않고 사용한다면 공통 풀(common pool)을 사용할 것이다. 이 경우, 우리 컴퓨팅 환경이 허용하는 한 외부 API에 제한 없이 요청을 보내게 될 것이다. 이렇게 되면 외부 API를 제공하는 서버에 허용치 이상으로 요청이 가게 되어 해당 서버의 컴퓨팅 속도가 느려질 수 있다. 또한 서비스 A가 장애를 일으키면서 응답이 지연되면 풀에 남은 사용 가능한 커넥션이 줄어들면서 서비스 B와 C의 연동도 대기 상태에 놓이게 되는 연쇄 지연 문제가 발생하게 된다. 따라서, 이러한 문제를 방지하기 위해 위 예시처럼 각 연동 API마다 별도의 스레드 풀을 지정하면 손쉽게 해결할 수 있다. 위의 경우에서는 서비스 A가 장애를 일으켜도 각 연동 서비스는 독립적인 스레드 풀을 갖기 때문에, 서비스 B와 C의 연동은 서비스 A의 상태에 영향을 받지 않는다. 또한 동시에 많은 요청이 들어오더라도, 스레드 풀에 지정된 스레드 수만큼만 동시에 작업을 처리할 수 있고, 나머지 요청들은 큐에서 대기하기 때문에 외부 API 서버에 가해지는 부하를 조절할 수 있다.
Q. 이외에 외부 API 사용시 고려해야될 점에는 무엇이 있을까요?
A. 외부 API 사용 시, 비동기 처리로 응답 시간 단축, 서킷 브레이커로 장애 대응, Rate Limit 적용으로 부하 관리, 타임아웃 설정으로 병목 방지, 그리고 캐싱을 통한 시스템 효율성 향상이 필요합니다.
'CS > 기술 면접 대비' 카테고리의 다른 글
[백엔드 기술 면접] #9 DB심화 (0) | 2024.11.24 |
---|---|
[백엔드 기술 면접] #8 JAVA (0) | 2024.11.23 |
[백엔드 기술 면접] #6 시스템 설계 (3) | 2024.11.20 |
[백엔드 기술 면접] #5 Security (0) | 2024.11.16 |
[백엔드 기술 면접] #4 Server (2) (3) | 2024.11.14 |