시니어 C# 게임 백엔드 면접 플레이북

ASP.NET Core를 중심으로 네 백엔드 생태계를 비교하고 게임 서버의 설계 판단을 연습합니다.

빠른 복습이 꺼져 있습니다.

프레임워크 비교

프레임워크 이름으로 승자를 정하지 말고, 병목과 팀의 운영 능력을 먼저 명시합니다.

1. 런타임과 동시성

한 줄 결론: I/O 대기와 CPU 작업을 분리하고, 각 런타임에서 공유 자원을 막는 코드가 무엇인지 설명할 수 있어야 합니다.

  • Node.js: JavaScript 콜백은 이벤트 루프에서 실행되고 일부 파일·DNS·암호화 작업은 libuv Worker Pool을 사용합니다. 둘 중 하나를 오래 점유하면 다른 요청의 지연이 커집니다.
  • NestJS: 동시성의 바닥은 Node.js와 같고, 모듈·가드·인터셉터 같은 구조가 추가됩니다. CPU 집약 작업은 워커나 별도 서비스로 경계를 둡니다.
  • Spring: Spring MVC는 Servlet 기반 요청 처리 모델이며, 비동기·논블로킹 체인이 실제 요구일 때 WebFlux를 선택합니다. 블로킹 저장소를 그대로 쓰면 WebFlux의 이점이 제한됩니다.
  • ASP.NET Core: 비동기 I/O 완료까지 스레드를 붙잡지 않고, 계속 작업은 ThreadPool에서 수행합니다. 동기 블로킹과 요청마다 무분별한 Task.Run은 피합니다.

시니어 면접 포인트: “동시 요청이 많다”와 “CPU 병렬 계산이 많다”를 구분하고, 이벤트 루프 지연·ThreadPool starvation·큐 길이·p95/p99 지연을 함께 관측한다고 답합니다.

2. 애플리케이션 구조와 DI

한 줄 결론: DI는 객체 생성을 숨기는 장치가 아니라 의존성 경계와 수명을 명시하는 설계 도구입니다.

  • Node.js: 순수 런타임은 구조를 강제하지 않아 함수 조합, 팩터리, 외부 컨테이너를 팀이 선택합니다. 자유도만큼 합의와 테스트 규율이 필요합니다.
  • NestJS: Module이 기능 경계를, Provider가 주입 대상을 표현합니다. 기본 singleton 외 request·transient scope는 비용과 전파 범위를 검토합니다.
  • Spring: IoC 컨테이너가 Bean 생성과 연결을 맡습니다. 기본 singleton Bean에 요청 상태를 보관하지 않고, 프록시와 스코프 경계를 이해합니다.
  • ASP.NET Core: 기본 컨테이너의 Singleton·Scoped·Transient DI 생명주기를 사용합니다. 요청 Scoped를 Singleton이 직접 캡처하지 않게 합니다.

시니어 면접 포인트: 프레임워크별 이름을 외우는 데서 끝내지 말고, 수명 불일치·숨은 서비스 로케이터·순환 의존성이 테스트 격리와 장애 범위에 미치는 영향을 설명합니다.

3. 데이터 접근

한 줄 결론: ORM은 반복 매핑을 줄이지만, 생성 SQL과 트랜잭션 경계를 이해해야 추상화 누수를 통제할 수 있습니다.

  • Node.js: Prisma는 스키마·생성 클라이언트 중심, TypeORM은 엔티티·리포지터리 중심 선택지를 제공합니다. 둘 다 실제 쿼리와 인덱스를 확인합니다.
  • NestJS: Prisma·TypeORM 등을 모듈과 Provider로 감싸 애플리케이션 계층과 격리하되, 과도한 범용 리포지터리로 쿼리 의도를 지우지 않습니다.
  • Spring: JPA/Hibernate의 영속성 컨텍스트와 지연 로딩, 변경 감지를 이해하고 N+1과 트랜잭션 밖 접근을 점검합니다.
  • ASP.NET Core: EF Core의 LINQ 번역·변경 추적·projection을 활용하되, 로그와 실행 계획으로 SQL 가시성을 유지합니다.

시니어 면접 포인트: “ORM 대 SQL” 이분법 대신 조회·명령 모델, 추적 필요 여부, 대량 쓰기, 인덱스, 잠금과 일관성 요구로 선택 근거를 제시합니다.

4. 성능 판단

한 줄 결론: 벤치마크 순위보다 실제 요청의 CPU·I/O·할당·직렬화 비중과 배포 조건을 재현하는 측정이 우선입니다.

  • Node.js: 이벤트 루프 지연, Worker Pool 포화, 힙과 GC, 네이티브 애드온 비용을 프로파일링합니다.
  • NestJS: Node 지표에 더해 DI scope, 파이프·가드·인터셉터, 직렬화 계층의 핫 패스를 측정합니다.
  • Spring: JVM 워밍업·GC·스레드 풀·Servlet/WebFlux 선택과 콜드 스타트 조건을 함께 봅니다.
  • ASP.NET Core: dotnet-counters·dotnet-trace·프로파일러로 할당, GC, ThreadPool, 예외, 직렬화 비용을 확인합니다.

시니어 면접 포인트: 동일 데이터·동일 외부 의존성·동일 컨테이너 제한에서 부하를 재현하고, 처리량과 꼬리 지연·오류율·비용을 함께 비교한다고 답합니다.

5. 테스트와 관측성

한 줄 결론: 테스트 가능성과 관측성은 부가 기능이 아니라 운영 중 가설을 빠르게 검증하는 설계 속성입니다.

  • Node.js: 함수·모듈 경계를 작게 두고, 구조화 로그와 Event Loop·메모리 메트릭, 비동기 추적 문맥을 연결합니다.
  • NestJS: TestingModule로 Provider를 교체하고, 가드·파이프·인터셉터는 단위 및 전 구간 테스트의 책임을 나눕니다.
  • Spring: 단위 테스트와 슬라이스·통합 테스트를 목적별로 구분하며, Micrometer·프로파일러·추적 문맥을 활용합니다.
  • ASP.NET Core: DI 대체와 TestServer/WebApplicationFactory 계열 격리를 사용하고, ILogger·Meter·Activity 기반 신호를 상관관계 ID로 잇습니다.

시니어 면접 포인트: 로그만 늘리지 말고 요청률·오류율·지연, 자원 포화, 프로파일 결과를 같은 배포 버전과 추적 ID로 연결하는 방식을 설명합니다.

6. 운영과 배포

한 줄 결론: 배포 단위의 크기보다 런타임 지원 정책, 종료 계약, 상태 점검, 복구 절차를 팀이 반복 실행할 수 있는지가 중요합니다.

  • Node.js: LTS 런타임과 lockfile을 고정하고, SIGTERM 수신 후 새 요청 중단·진행 요청 종료·강제 종료 제한을 둡니다.
  • NestJS: Node 운영 기준에 lifecycle hook과 어댑터 설정을 더하며, readiness에서 실제 제공 가능 상태만 노출합니다.
  • Spring: JDK·프레임워크 조합, JVM 메모리 제한, graceful shutdown, Actuator 기반 상태 점검을 배포 계약에 포함합니다.
  • ASP.NET Core: .NET 지원 버전, 컨테이너 메모리, Host 종료 토큰, Kestrel·프록시 제한, health check를 조율합니다.

시니어 면접 포인트: 네 프레임워크 모두 무상태 요청 계층은 수평 확장하기 쉽지만, 세션·락·인메모리 캐시·백그라운드 작업의 소유권을 외부화하거나 샤딩해야 한다고 짚습니다.

7. 모바일 게임 적합성

한 줄 결론: ASP.NET Core는 C# 역량을 활용하기 좋지만, Unity 코드 공유 자체를 목표로 삼기보다 서버 권한·배포 독립성·운영 경험을 우선합니다.

결정 축Node.jsNestJSSpringASP.NET Core
팀 역량·채용JavaScript 런타임·비동기 운영 역량TypeScript와 구조화된 서버 개발 경험Java/JVM 운영 인력과 성숙한 생태계C# 백엔드 역량과 .NET 운영 경험
Unity 연계언어 중립 계약 생성DTO 계약을 모듈 경계에 배치언어 중립 계약 생성DTO·규칙 일부 공유 가능하나 클라이언트를 신뢰 경계 안에 두지 않음
실시간 경계I/O 게이트웨이와 CPU 작업 격리게이트웨이·가드·인터셉터 구조 활용MVC 또는 WebFlux를 의존성 특성에 맞게 선택SignalR·소켓과 일반 API를 분리하고 상태 소유권 명시
비실시간작은 서비스·운영 도구 구성상점·운영 도구를 모듈화복잡한 도메인·트랜잭션 경험 활용계정·결제·우편·랭킹 API에 C# 생산성 활용

시니어 면접 포인트: 최종 선택은 기능 적합성뿐 아니라 온콜 경험, 관측 도구, 보안 패치, 채용 시장, 기존 데이터 계층과의 통합 비용을 가중치로 둔 결정 행렬로 제시합니다.

ASP.NET Core 심층 학습

1. Kestrel, Host, 리버스 프록시

개념

WebApplication/Generic Host는 DI·구성·로깅·애플리케이션 수명을 묶고, Kestrel은 HTTP 요청을 HttpContext로 노출합니다. Kestrel은 직접 공개할 수도 있고 IIS·Nginx 같은 리버스 프록시 뒤에 둘 수도 있습니다.

실무 함정

프록시의 원본 IP·스킴 전달을 신뢰 네트워크 없이 허용하거나, 프록시와 Kestrel의 본문 크기·타임아웃 제한을 다르게 두면 보안과 장애 분석이 흐려집니다. 종료 시 readiness를 먼저 내리고 Host 종료 토큰 안에서 진행 요청과 백그라운드 작업을 정리합니다.

게임 서버 적용

점검 배포에서는 새 로그인·매칭 요청을 차단하고, 재화 저장과 큐 메시지 확정처럼 소유 중인 작업에 명시적 종료 기한을 둡니다.

30초 답변

“Kestrel은 HTTP 서버, Host는 구성·DI·로깅·수명 관리자입니다. 프록시 경계를 명시하고 readiness와 graceful shutdown을 연결해 배포 중 손실을 줄입니다.”

2분 답변

프록시가 TLS 종료·라우팅·보호 기능을 맡을 수 있지만 애플리케이션도 전달 헤더 신뢰 범위와 요청 제한을 알아야 합니다. 종료 신호가 오면 트래픽 유입을 멈추고, 취소 토큰을 작업에 전달하며, 제한 시간 이후 재처리 가능한 상태로 남깁니다. 종료 시간과 강제 종료 건수를 배포 지표로 검증합니다.

2. 미들웨어 순서와 scoped 의존성

개념

미들웨어는 등록 순서로 요청을 통과하고 역순으로 응답을 통과합니다. 다음 구성 요소를 호출하지 않으면 단락됩니다. 전역 예외 처리는 앞쪽에, 인증은 인가보다 앞에 두고 라우팅 메타데이터가 필요한 구성 요소는 라우팅 이후에 둡니다.

실무 함정

일반 미들웨어 인스턴스는 앱 수명 동안 재사용될 수 있으므로 scoped 서비스를 생성자에 캡처하지 않습니다. 요청별 의존성은 InvokeAsync 매개변수로 받거나 factory 기반 미들웨어를 사용합니다.

public sealed class PlayerContextMiddleware(RequestDelegate next)
{
    public async Task InvokeAsync(HttpContext context, IPlayerSession session)
    {
        await session.LoadAsync(context.RequestAborted);
        await next(context);
    }
}

게임 서버 적용

상관관계 ID → 예외 처리 → 인증 → 인가 → endpoint 순서를 문서화하고, 점검 단락 응답에도 동일한 오류 계약과 추적 ID를 제공합니다.

30초 답변

“순서가 동작입니다. 예외 처리는 바깥쪽, 인증은 인가 앞에 두며, scoped 의존성은 요청 시점에 해석해 captive dependency를 막습니다.”

2분 답변

각 미들웨어의 전제와 단락 조건을 표로 관리합니다. 인증 전 사용자 정보가 필요하거나 예외 처리 뒤에서 오류가 새는 순서 문제를 통합 테스트로 검증하고, 매 요청 새 scope의 서비스는 InvokeAsync 주입을 사용합니다.

3. DI 생명주기와 자원 소유권

개념

Singleton은 컨테이너 수명, Scoped는 웹 요청 단위, Transient는 해석할 때마다 생성됩니다. 컨테이너가 만든 IDisposable은 컨테이너가 해당 scope 종료 시 정리합니다.

실무 함정

Singleton이 Scoped를 직접 가지는 captive dependency는 요청 상태를 잘못 공유합니다. 임시 disposable을 루트 컨테이너에서 반복 해석하지 말고, 필요 수명이 짧으면 팩터리나 명시적 scope를 설계합니다.

게임 서버 적용

DbContext와 플레이어 요청 문맥은 Scoped, 불변 설정과 thread-safe 클라이언트는 Singleton 후보입니다. 백그라운드 Singleton 서비스에서 Scoped가 필요하면 IServiceScopeFactory로 작업마다 scope를 만들고 즉시 정리합니다.

30초 답변

“수명은 상태와 자원 소유권 계약입니다. 긴 수명의 서비스가 짧은 수명을 캡처하지 않게 하고, 컨테이너가 만든 disposable은 컨테이너에 정리를 맡깁니다.”

2분 답변

수명 선택 때 thread safety, 요청 격리, 연결 소유권을 봅니다. scope validation을 켜고, 백그라운드 작업은 IServiceScopeFactory로 별도 scope를 만든 뒤 작업 종료와 함께 폐기합니다. 서비스 로케이터 대신 생성자 의존성으로 계약을 드러냅니다.

4. async/await, ThreadPool, 취소

개념

비동기 I/O는 대기 중 스레드를 반환해 동시 처리 여지를 높입니다. CancellationToken은 강제 종료가 아니라 호출자가 더 이상 결과를 원하지 않는다는 협력 신호이며, 타임아웃 정책과 요청 취소를 하위 호출에 전달합니다.

실무 함정

.Result·Wait()와 동기 I/O는 ThreadPool starvation과 꼬리 지연을 유발할 수 있습니다. CPU 작업을 요청마다 Task.Run으로 감싸는 것은 용량을 늘리지 않으므로 bounded queue나 별도 워커를 검토합니다.

app.MapPost("/players/{id}/reward", async (
    string id, RewardRequest request, GameDbContext db,
    CancellationToken cancellationToken) =>
{
    var player = await db.Players.SingleAsync(
        p => p.Id == id, cancellationToken);
    player.Grant(request.ItemId);
    await db.SaveChangesAsync(cancellationToken);
    return Results.NoContent();
});

게임 서버 적용

클라이언트 연결이 끊긴 조회·검증은 중단하되, 이미 외부 결제가 확정된 보상은 단순 취소하지 않고 멱등 키와 비동기 보상 흐름으로 완결합니다.

30초 답변

“async는 스레드를 더 만드는 기능이 아니라 I/O 대기 중 반환하는 모델입니다. 요청 취소와 타임아웃을 DB·HTTP 호출까지 전달하고, 커밋 경계는 업무 의미에 맞게 정합니다.”

2분 답변

전체 호출 스택을 비동기로 유지하고 ThreadPool queue·thread count·지연을 봅니다. 취소는 OperationCanceledException을 정상 경로와 구분해 기록하며, 취소 가능한 읽기와 반드시 완결해야 할 커밋 후 작업을 나눕니다.

5. GC, LOH, 할당 관리

개념

.NET GC는 세대별 수집으로 짧게 사는 객체를 효율적으로 회수합니다. 큰 객체는 LOH에 배치되고 Gen 2 수집과 연관되어 일시 정지와 메모리 비용에 영향을 줄 수 있습니다.

실무 함정

측정 없이 struct·Span·pooling을 늘리면 복잡성과 데이터 잔류 위험만 커질 수 있습니다. ArrayPool<T>로 빌린 버퍼는 반드시 반환하고, 민감 정보 초기화와 실제 사용 길이를 관리합니다.

게임 서버 적용

랭킹 직렬화·대량 패킷·스냅샷 생성의 할당률, Gen 2/LOH 크기, GC pause를 부하에서 먼저 측정한 뒤 재사용·스트리밍·페이지네이션을 적용합니다.

30초 답변

“GC 튜닝은 할당 프로파일에서 시작합니다. 핫 패스의 큰 임시 버퍼를 줄이고 필요하면 ArrayPool을 쓰되, 반환과 데이터 정리까지 소유권으로 관리합니다.”

2분 답변

할당률과 pause가 SLO에 실제 영향을 주는지 확인하고 call stack을 찾습니다. 작은 객체의 수명 단축, projection·스트리밍, 큰 버퍼 재사용 순으로 바꾸며 변경 전후 꼬리 지연과 메모리를 비교합니다.

6. EF Core 조회 성능

개념

추적 쿼리는 엔티티 원본 값과 identity resolution을 관리해 SaveChanges에 사용합니다. 읽기 전용은 AsNoTracking과 DTO projection으로 필요한 열만 가져오는 선택을 검토합니다.

실무 함정

지연 로딩이나 반복 탐색으로 생기는 N+1, 무제한 Include, 클라이언트가 필요 없는 전체 엔티티 반환을 피합니다. compiled query는 쿼리 컴파일 비용이 병목으로 측정되고 모양이 반복될 때만 적용합니다.

게임 서버 적용

우편함·전투 기록은 페이지네이션과 projection을 쓰고, SQL 로그·실행 계획·왕복 횟수로 인덱스와 쿼리 형태를 검증합니다.

30초 답변

“읽기는 기본적으로 필요한 열만 projection하고 추적 필요성을 명시합니다. N+1은 SQL 왕복을 관측해 없애고 compiled query는 측정된 반복 핫 패스에만 씁니다.”

2분 답변

추적은 업데이트 편의와 identity resolution 이점도 있으므로 항상 끄는 규칙은 피합니다. 데이터 크기·중복·왕복·생성 SQL을 비교하고, 먼저 쿼리와 인덱스를 개선한 뒤 컴파일 비용이 남으면 compiled query를 검토합니다.

7. 트랜잭션과 낙관적 동시성

개념

한 번의 SaveChanges는 지원되는 관계형 공급자에서 기본적으로 트랜잭션으로 실행됩니다. 동시성 토큰은 읽은 값이 갱신 전까지 유지됐는지 조건에 포함하고, 충돌 시 DbUpdateConcurrencyException으로 드러납니다.

실무 함정

트랜잭션 안에서 원격 API를 기다리거나 사용자 입력을 기다리면 잠금 시간이 길어집니다. 격리 수준을 높이는 것만으로 모든 업무 경쟁이 해결되지 않으며 재시도 시 업무 규칙을 다시 검증해야 합니다.

for (var attempt = 0; attempt < 3; attempt++)
{
    try
    {
        player.Spend(cost);
        await db.SaveChangesAsync(cancellationToken);
        break;
    }
    catch (DbUpdateConcurrencyException) when (attempt < 2)
    {
        await db.Entry(player).ReloadAsync(cancellationToken);
        player.ValidateCanSpend(cost);
    }
}

게임 서버 적용

재화 차감은 짧은 DB 트랜잭션과 동시성 토큰을 사용하고, 요청 멱등 키·원장 유일 제약으로 네트워크 재시도까지 방어합니다.

30초 답변

“동시성 토큰으로 lost update를 감지하고, 충돌하면 최신 값을 다시 읽어 업무 규칙을 재검증합니다. 트랜잭션은 짧게 유지하고 외부 호출은 분리합니다.”

2분 답변

격리 수준, 동시성 토큰, 유일 제약은 서로 다른 실패를 막습니다. 재화처럼 경쟁이 많은 경로는 원장과 멱등 키를 함께 두며, 제한 재시도와 충돌 지표를 운영합니다. 재시도 후에도 실패하면 사용자에게 안전한 재요청 계약을 제공합니다.

8. 보안과 Rate Limiting

개념

인증은 주체를 확인하고 인가는 해당 자원·행동 권한을 판단합니다. 입력 검증, 출력 인코딩, 비밀 관리, 최소 권한을 계층별로 적용하며 ASP.NET Core Rate Limiting 정책으로 공정성과 애플리케이션 안정성을 보조합니다.

실무 함정

속도 제한은 대규모 DDoS를 애플리케이션 하나가 막는 장치가 아닙니다. CDN·WAF·네트워크 계층 보호와 함께 쓰고, 프록시 IP 신뢰와 사용자·기기·엔드포인트별 키의 우회 가능성을 검토합니다.

게임 서버 적용

로그인·쿠폰·닉네임 검색에 서로 다른 정책을 두고, 공격 시 정상 이용자를 모두 차단하지 않도록 계정·IP·디바이스 신호와 단계적 거부를 조합합니다. 비밀은 소스와 이미지에 넣지 않습니다.

30초 답변

“인증 후 자원 단위 인가를 하고, 입력은 서버가 다시 검증합니다. Rate Limiting은 남용과 자원 고갈 완화용이며 DDoS는 엣지·네트워크 보호와 함께 대응합니다.”

2분 답변

신뢰 경계를 클라이언트, 프록시, API, 데이터 저장소로 나눕니다. 정책 키와 한도를 엔드포인트 위험에 맞추고 429·재시도 정보와 지표를 제공합니다. 비밀 회전, 감사 로그, 최소 권한, 침해 시 폐기 절차까지 운영 계약으로 설명합니다.

9. 캐시와 일관성

개념

IMemoryCache는 프로세스 로컬이라 인스턴스 간 공유되지 않고, Redis 같은 분산 캐시는 여러 인스턴스가 공유할 수 있습니다. 캐시는 원본 데이터의 소유자가 아니며 만료·무효화·장애 시 동작을 함께 설계합니다.

실무 함정

동시 만료 때 원본으로 몰리는 캐시 스탬피드를 막기 위해 single-flight, 만료 지터, stale-while-revalidate 같은 전략을 선택합니다. 분산 캐시를 쓰면 네트워크 지연과 직렬화, 부분 실패가 추가됩니다.

게임 서버 적용

정적 마스터 데이터는 버전 키로 캐시하기 쉽지만, 재화 잔액처럼 강한 일관성이 필요한 값은 캐시를 권위 저장소로 취급하지 않습니다.

30초 답변

“로컬 캐시는 빠르지만 인스턴스별이고, 분산 캐시는 공유되지만 네트워크 실패가 생깁니다. 데이터별 허용 가능한 stale 시간과 무효화 주체부터 정합니다.”

2분 답변

cache-aside에서 miss·만료·원본 장애를 분리해 관측합니다. 인기 키는 single-flight와 지터로 보호하고, 쓰기 후 무효화 실패를 대비해 버전 키나 이벤트를 사용합니다. 적중률만 아니라 원본 부하와 stale 오류를 봅니다.

10. 관측성, OpenTelemetry 분산 추적, SLO

개념

구조화 로깅은 검색 가능한 속성을 남기고, RED는 요청률·오류·지연, USE는 자원 사용률·포화·오류를 봅니다. OpenTelemetry의 trace·metric·log 상관관계로 서비스 경계를 잇습니다.

실무 함정

플레이어 ID·토큰·채팅 원문 같은 민감 정보를 무분별하게 기록하지 않습니다. 모든 요청의 고카디널리티 label과 전체 trace를 보존하면 비용과 조회 품질이 악화되므로 샘플링·보존·마스킹 정책을 둡니다.

게임 서버 적용

로그인 성공률, 매칭 지연, 재화 처리 오류율을 사용자 여정 SLI로 정의하고, SLO 오류 예산 소진 시 배포 중지와 롤백·완화 runbook으로 연결합니다.

30초 답변

“로그·메트릭·trace를 배포 버전과 상관관계 ID로 연결하고, 사용자 관점 SLO를 경보 기준으로 씁니다. 장애 때는 범위 확인, 완화, 원인 분석 순서입니다.”

2분 답변

RED로 API 증상을, USE로 자원 원인을 좁히고 trace로 느린 의존성을 찾습니다. 경보는 SLO 소진 속도에 맞춰 잡음을 줄이며, 먼저 롤백·기능 차단·용량 보호로 영향을 완화합니다. 사후에는 타임라인과 탐지·복구 지연을 개선 과제로 남깁니다.

모바일 게임 서버 시나리오

설계 답변은 요구를 먼저 좁히고, 정합성·실패·관측·확장의 순서로 검증합니다.

1. 출시일 로그인 폭주

요구사항 질문

예상 동시 접속, 허용 대기 시간, 익명·기존 사용자 우선순위, 목표 성공률과 DB의 안전 처리량을 확인합니다.

핵심 설계

CDN·WAF 뒤 입구 Rate Limiting과 용량이 제한된 대기열을 두고, 인증 확인과 토큰 발급 경로를 분리합니다. 클라이언트에는 지수 백오프와 jitter가 포함된 재시도 시점을 줍니다.

정합성

로그인 시 쓰기를 최소화하고 세션·토큰 발급은 요청 ID로 중복을 식별합니다. 대기 순서는 절대 공정성보다 등급별 정책과 만료 규칙을 명시합니다.

실패 모드

인증 저장소가 느려지면 입구 허용량을 낮추고 신규 로그인을 대기시킵니다. 무제한 재시도와 열린 연결이 DB 연결 풀을 고갈시키지 않게 합니다.

관측 지표

대기열 길이·예상 대기 시간, 허용·거부·로그인 성공률, DB 연결 풀·CPU, 토큰 발급 p95/p99와 재시도율을 봅니다.

확장 전략

무상태 입구와 토큰 발급기를 수평 확장하되, DB 안전 한도를 기준으로 전체 허용률을 조정합니다. 지역·플랫폼별 셀로 장애 범위를 줄입니다.

꼬리 질문

  • 대기열 서버 자체가 병목이면 어떤 정보를 근사치로 제공합니까?
  • VIP 우선순위가 일반 사용자 기아를 만들지 않게 하려면?

2. 인벤토리 재화 지급

요구사항 질문

재화가 소모성인지, 음수 허용 여부, 지급 근거와 감사 보존 기간, 한 플레이어의 최대 쓰기 빈도를 묻습니다.

핵심 설계

명령 ID를 멱등 키로 받고 DB 유니크 제약으로 중복을 차단합니다. 불변 원장을 먼저 기록하고 현재 잔액은 원장과 같은 짧은 트랜잭션에서 갱신합니다.

정합성

플레이어 버전 열로 낙관적 동시성을 적용하고 충돌 시 최신 잔액에서 규칙을 다시 검증합니다. 원장 합계와 잔액을 주기적으로 대조합니다.

실패 모드

응답 유실 뒤 같은 명령이 오면 기존 결과를 반환합니다. 원장 저장은 성공하고 후속 알림이 실패하면 outbox에서 재처리합니다.

관측 지표

멱등 중복률, 동시성 충돌률, 원장·잔액 불일치, 트랜잭션 p99, 플레이어별 쓰기 편향과 outbox 적체를 봅니다.

확장 전략

플레이어 ID로 쓰기 소유권을 샤딩하고 핫 플레이어를 제한합니다. 읽기 모델은 비동기로 만들되 지급 확인은 권위 저장소 결과를 사용합니다.

꼬리 질문

  • 명령 ID 보존 기간이 끝난 뒤 재요청은 어떻게 처리합니까?
  • 원장과 잔액 불일치가 발견되면 어느 쪽을 기준으로 복구합니까?

3. 실시간 랭킹과 시즌 정산

요구사항 질문

순위 갱신 빈도, 동점 규칙, 상위·내 주변 조회, 시즌 종료 시점의 정확성 및 보상 완료 기한을 확인합니다.

핵심 설계

Redis Sorted Set은 실시간 점수·순위 조회에 사용하고, 모든 점수 변경은 영속 원장에 기록합니다. 시즌 마감에는 입력을 버전으로 봉인한 스냅샷을 만듭니다.

정합성

이벤트 ID로 중복 갱신을 막고 동점 키를 고정합니다. 최종 보상은 Redis 현재값이 아니라 재현 가능한 마감 스냅샷을 기준으로 지급합니다.

실패 모드

Redis 장애 시 최근 스냅샷이나 제한된 조회로 강등하고 영속 이벤트에서 재구축합니다. 마감 중 지연 이벤트의 포함 기준을 시즌 버전으로 판정합니다.

관측 지표

갱신 지연, Redis 메모리·명령 p99, 원장 대비 점수 불일치, 스냅샷 소요 시간, 보상 적체와 중복 지급률을 봅니다.

확장 전략

게임 모드·리전별 보드를 분할하고 상위권 병합은 별도 집계 계층에서 수행합니다. 과거 시즌은 불변 저장소로 내려 온라인 집합을 작게 유지합니다.

꼬리 질문

  • 마감 직전 늦게 도착한 승리 이벤트를 어디까지 인정합니까?
  • 수백만 명의 내 주변 순위를 효율적으로 조회하려면?

4. 앱스토어 결제 검증

요구사항 질문

지원 스토어, 구독·소모품 유형, 환불과 보류 상태, 검증 지연 허용치, 고객 지원의 수동 복구 권한을 확인합니다.

핵심 설계

클라이언트 영수증을 신뢰하지 않고 스토어 서버에서 검증합니다. 영수증·거래 ID에 유일 제약을 두고 검증 결과, 보상 명령, outbox를 원자적으로 저장합니다.

정합성

동일 거래는 한 번만 보상하고 상태 전이는 단조롭게 관리합니다. 환불은 원거래를 참조하는 보상 원장으로 남겨 감사 가능하게 합니다.

실패 모드

스토어 타임아웃은 지급 실패로 확정하지 않고 보류 큐에서 제한 재시도합니다. DB 커밋 뒤 응답 유실은 거래 ID 조회로 기존 결과를 반환합니다.

관측 지표

스토어별 검증 성공·보류·거부율, 지연 p99, 중복 거래 차단, 보상 outbox 나이, 환불 처리량과 수동 개입 건수를 봅니다.

확장 전략

스토어 어댑터를 격리하고 검증 워커를 수평 확장합니다. 재시도는 jitter와 회로 차단을 쓰며 거래 ID 기준 파티션으로 순서를 유지합니다.

꼬리 질문

  • 스토어 검증 API가 몇 시간 중단되면 사용자 경험을 어떻게 설계합니까?
  • 환불 후 이미 소비한 재화는 어떤 정책으로 처리합니까?

5. 라이브 이벤트 무중단 배포

요구사항 질문

대상 세그먼트, 시작·종료의 시간 정확도, 클라이언트 최소 버전, 긴급 중단 권한과 롤백 허용 시간을 묻습니다.

핵심 설계

불변 설정 버전과 스키마 검증을 두고 피처 플래그로 내부·일부 리전·전체 순서로 단계적 출시합니다. 모든 변경은 작성자·승인자와 함께 감사 기록에 남깁니다.

정합성

요청 동안 같은 설정 버전을 사용하고 보상 명령에 이벤트 버전을 기록합니다. 서버 시간이 권위이며 중복 참여는 유일 제약으로 막습니다.

실패 모드

오류율이나 경제 지표가 임계치를 넘으면 kill switch로 신규 참여를 막고 이전 설정 버전으로 롤백합니다. 이미 지급된 보상은 삭제하지 않고 보상 정책을 적용합니다.

관측 지표

설정 버전별 참여·완료·오류율, 보상 발행량, 경제 인플레이션 신호, 플래그 전파 지연과 롤백 소요 시간을 봅니다.

확장 전략

버전 설정을 CDN·로컬 캐시에 배포하되 짧은 전파 지연을 계약으로 둡니다. 세그먼트 계산은 미리 구체화하고 핫 경로에서 복잡한 규칙 평가를 피합니다.

꼬리 질문

  • 두 설정 버전이 동시에 보일 때 보상 일관성을 어떻게 지킵니까?
  • 잘못 지급된 이벤트 보상을 회수할 기준은 무엇입니까?

6. 치팅과 리플레이 공격 방어

요구사항 질문

보호할 가치, 클라이언트 변조 가능성, 허용 시계 오차, 차단 전 증거 수준과 오탐 시 복구 절차를 확인합니다.

핵심 설계

요청에 nonce·timestamp·signature를 검증하고 짧은 재사용 창을 둡니다. 결과·재화·쿨다운은 서버 권위 데이터로 다시 계산하며 위험 행위는 속도 제한합니다.

정합성

nonce 소비와 명령 처리를 같은 원자 경계 또는 멱등 기록으로 연결합니다. 서명은 기기 신뢰의 보조 신호일 뿐 클라이언트 값을 권위로 만들지 않습니다.

실패 모드

캐시 장애로 nonce 확인이 불가능하면 고위험 거래는 보류하고 저위험 읽기는 제한 허용합니다. 즉시 영구 차단보다 격리·추가 검증으로 오탐 피해를 줄입니다.

관측 지표

서명 실패·nonce 재사용·시간 오차 분포, 계정별 행동 속도, 탐지 규칙별 정탐·오탐, 이의 제기와 해제 시간을 봅니다.

확장 전략

저비용 규칙은 입구에서, 행동 그래프와 이상 탐지는 비동기 파이프라인에서 수행합니다. 규칙 버전을 기록해 판정 재현성과 단계적 출시를 보장합니다.

꼬리 질문

  • 모바일 기기 시계가 크게 틀린 정상 사용자는 어떻게 처리합니까?
  • 서명 비밀이 유출됐을 때 회전과 하위 호환은 어떻게 합니까?

7. 멀티 리전 플레이어 상태

요구사항 질문

지역별 지연 SLO, 데이터 주권, 허용 가능한 RPO·RTO, 지역 이동 빈도와 전체 리전 장애 시 기능 우선순위를 묻습니다.

핵심 설계

플레이어마다 홈 리전을 지정하고 상태 쓰기 소유권을 한 리전에 둡니다. 타 리전은 홈으로 라우팅하며 읽기 모델과 복구 사본은 비동기 복제합니다.

정합성

epoch가 포함된 소유권 토큰으로 이전 리전의 늦은 쓰기를 거부합니다. 홈 이전은 쓰기 동결·복제 확인·소유권 전환의 명시적 절차로 수행합니다.

실패 모드

리전 간 단절 때 양쪽 쓰기를 열지 않고 비소유 리전은 읽기 전용 또는 제한 기능으로 강등합니다. 승격은 합의된 RPO 손실과 재조정 절차를 따릅니다.

관측 지표

홈 라우팅 지연, 복제 lag, epoch 거부, 리전별 오류 예산, 장애 감지·승격·복구 시간과 실제 RPO를 측정합니다.

확장 전략

플레이어를 리전 안에서 다시 샤딩하고 인기 길드 같은 교차 플레이어 기능은 별도 소유권을 둡니다. 셀 단위 배포로 부분 장애 범위를 제한합니다.

꼬리 질문

  • 홈 리전 복구 뒤 임시 리전의 변경을 어떻게 재조정합니까?
  • 길드 전쟁처럼 여러 홈 리전을 묶는 쓰기는 어떻게 설계합니까?

실전 면접 질문 40

버튼의 질문을 먼저 소리 내어 답한 뒤, 판단 기준과 위험 신호를 비교합니다.

먼저 생각하기

CPU 작업과 비동기 I/O, continuation 실행을 구분합니다.

모범 답변

async/await는 비동기 작업의 연속을 표현하는 언어 기능이지 새 스레드를 만드는 명령이 아닙니다. 소켓 I/O는 완료를 기다리는 동안 요청 스레드를 점유하지 않고, 완료 뒤 continuation이 실행됩니다. CPU 작업은 여전히 CPU를 쓰며 Task.Run은 ThreadPool 작업을 추가할 뿐이므로 서버 용량을 자동으로 늘리지 않습니다.

꼬리 질문

  • ASP.NET Core에서 continuation은 보통 어디에서 실행됩니까?
  • 요청 처리에서 Task.Run을 남용하면 어떤 문제가 생깁니까?

위험한 답변

await마다 새 스레드가 생기고 CPU 작업도 자동으로 빨라진다고 단정하는 답변입니다.

먼저 생각하기

CPU 포화와 스레드 부족을 지표로 구분합니다.

모범 답변

starvation이면 CPU가 남아도 요청 지연과 ThreadPool 큐가 늘고 스레드 수가 증가합니다. dotnet-counters와 trace로 queue length, thread count, 대기 스택을 확인해 .Result·Wait·동기 I/O를 찾습니다. 호출 경로를 끝까지 비동기로 바꾸고 CPU 작업은 용량이 제한된 워커 큐로 격리한 뒤 부하에서 p99 개선을 검증합니다.

꼬리 질문

  • CPU 100% 상황과 어떤 지표로 구분합니까?
  • 최소 스레드 수 증가는 언제 임시 완화가 됩니까?

위험한 답변

원인 조사 없이 ThreadPool 최소 스레드만 크게 올리면 해결된다는 답변입니다.

먼저 생각하기

할당률, 객체 수명, pause와 꼬리 지연을 연결합니다.

모범 답변

짧게 사는 객체는 주로 Gen 0에서 회수되고 살아남으면 상위 세대로 승격됩니다. 큰 객체는 LOH에 배치되어 Gen 2 수집과 연관되므로 대형 직렬화 버퍼가 반복되면 메모리와 pause가 p99에 영향을 줄 수 있습니다. 할당 프로파일과 GC pause를 먼저 측정하고 projection·스트리밍·안전한 버퍼 재사용 순서로 줄입니다.

꼬리 질문

  • ArrayPool 사용 시 데이터 보안 위험은 무엇입니까?
  • Server GC 선택을 어떤 부하에서 검증합니까?

위험한 답변

LOH 객체는 절대 수집되지 않거나 GC가 항상 전체 애플리케이션을 오래 멈춘다는 답변입니다.

먼저 생각하기

동기 범위와 비동기 경계를 먼저 구분합니다.

모범 답변

Span<T>는 연속 메모리의 일부를 복사 없이 동기 핫 패스에서 다룰 때 유용하며 ref struct라 힙 저장과 await 경계를 넘을 수 없습니다. 비동기 API나 장기 보관에는 Memory<T>가 맞지만 소유권과 수명을 분명히 해야 합니다. 프로파일에서 복사·할당이 병목일 때만 적용하고 일반 DTO 코드의 가독성을 희생하지 않습니다.

꼬리 질문

  • ReadOnlySpan은 문자열 파싱에 어떤 이점이 있습니까?
  • Memory를 반환할 때 버퍼 소유권은 누가 가집니까?

위험한 답변

모든 배열을 Span으로 바꾸면 무조건 빨라지고 비동기 메서드에도 그대로 저장할 수 있다는 답변입니다.

먼저 생각하기

수명 불일치와 요청 상태 공유를 설명합니다.

모범 답변

Singleton이 Scoped를 캡처하면 첫 scope의 DbContext나 요청 상태가 애플리케이션 수명까지 남아 다른 요청과 공유될 수 있습니다. 이는 동시성 오류, 이미 폐기된 객체 접근, 추적 엔티티 누적을 만듭니다. scope validation으로 조기에 찾고, 백그라운드 Singleton은 IServiceScopeFactory로 작업마다 scope를 생성해 즉시 폐기합니다.

꼬리 질문

  • Transient를 Singleton이 캡처하는 경우는 항상 안전합니까?
  • 미들웨어에서 Scoped를 어떻게 주입합니까?

위험한 답변

DI 컨테이너가 수명을 자동 조정하므로 어떤 방향의 의존도 안전하다는 답변입니다.

먼저 생각하기

예외 비용과 계약 가시성을 함께 봅니다.

모범 답변

예외는 드문 실패를 호출 경계 밖으로 전달하는 수단이라 빈번한 분기마다 쓰면 스택 캡처·로깅 비용과 가독성 저하가 생깁니다. 재화 부족처럼 예상 가능한 결과는 명시적 결과 타입이나 도메인 오류로 표현하고, 불변식 위반과 인프라 실패는 예외로 둡니다. 예외율을 메트릭으로 보므로 정상 상황이 경보를 오염시키지 않게 합니다.

꼬리 질문

  • TryParse 패턴이 적합한 경계는 어디입니까?
  • 전역 예외 처리에서 어떤 정보를 숨겨야 합니까?

위험한 답변

예외는 성능 비용이 있으므로 어떤 상황에서도 던지면 안 된다는 답변입니다.

먼저 생각하기

요청 취소, 타임아웃, 커밋 의미를 구분합니다.

모범 답변

엔드포인트의 RequestAborted 또는 바인딩된 CancellationToken을 서비스, HTTP, EF Core 비동기 호출까지 같은 작업 문맥으로 전달합니다. 별도 타임아웃이 필요하면 linked token을 만들고 소유자가 폐기합니다. 다만 외부 결제가 확정된 뒤 보상 저장처럼 반드시 완결할 작업은 요청 취소와 분리해 멱등한 백그라운드 흐름으로 넘깁니다.

꼬리 질문

  • OperationCanceledException을 오류 로그로 남겨야 합니까?
  • DB 커밋 중 취소 결과가 불명확하면 어떻게 합니까?

위험한 답변

취소 토큰은 실행 중인 코드를 강제로 중단하며 모든 메서드에서 새 토큰을 만들면 된다는 답변입니다.

먼저 생각하기

개별 연산 안전성과 업무 원자성을 나눕니다.

모범 답변

ConcurrentDictionary는 개별 컬렉션 연산과 내부 구조를 안전하게 하지만 여러 단계의 업무 불변식을 자동으로 원자화하지 않습니다. 조회 후 잔액 검사와 차감처럼 복합 연산은 한 번의 원자 API, 잠금, immutable 상태 교환 또는 DB 트랜잭션으로 보호해야 합니다. 팩터리 델리게이트가 여러 번 호출될 가능성도 고려해 부수 효과를 넣지 않습니다.

꼬리 질문

  • GetOrAdd 팩터리에 외부 호출을 넣으면 왜 위험합니까?
  • 프로세스가 여러 대면 어떤 보호가 더 필요합니까?

위험한 답변

컬렉션 이름에 Concurrent가 붙으면 그 주변의 모든 비즈니스 로직도 원자적이라는 답변입니다.

먼저 생각하기

관리 힙, 네이티브 메모리, 정상 캐시 성장을 구분합니다.

모범 답변

먼저 배포 버전별 working set, GC heap, LOH, allocation rate와 Gen 2 이후 생존량의 추세를 비교합니다. 제한된 진단 창에서 dump나 gcdump를 수집해 지배 객체와 GC root를 찾고 이벤트 구독, 정적 컬렉션, 무제한 캐시를 확인합니다. 네이티브 메모리와 컨테이너 제한도 분리한 뒤 재현 부하로 수정 전후 기울기를 검증합니다.

꼬리 질문

  • 메모리가 높지만 안정적이면 누수입니까?
  • dump에 민감 정보가 있을 때 어떻게 다룹니까?

위험한 답변

GC 언어에는 누수가 없으므로 주기적으로 프로세스만 재시작하면 된다는 답변입니다.

먼저 생각하기

SLO, 병목 가설, 재현 부하, 회귀를 순서화합니다.

모범 답변

사용자 SLO와 현재 요청률·오류율·p95/p99를 기준선으로 잡고 CPU, I/O, 잠금, 할당 중 병목 가설을 세웁니다. 프로파일과 trace로 핫 경로를 확인한 뒤 한 변수만 바꾸고 같은 데이터·용량 제한·워밍업 조건에서 비교합니다. 처리량뿐 아니라 오류, 자원 비용, 회귀와 운영 복잡성까지 본 뒤 작은 비율로 출시합니다.

꼬리 질문

  • 마이크로벤치마크 결과를 운영에 어떻게 연결합니까?
  • 평균 지연 개선만으로 충분하지 않은 이유는?

위험한 답변

유명한 최적화 기법을 먼저 적용하고 평균 응답 시간 하나만 비교하는 답변입니다.

먼저 생각하기

요청 정순·응답 역순과 각 전제를 말합니다.

모범 답변

미들웨어는 등록 순서로 요청을 받고 역순으로 응답을 처리하며 다음을 호출하지 않으면 단락됩니다. 전역 예외 처리는 바깥쪽에 두고 라우팅 메타데이터가 필요한 구성은 라우팅 뒤, 인증은 인가 앞에 둡니다. 순서가 바뀌면 사용자 정보 없이 인가하거나 예외가 표준 오류 계약 밖으로 새므로 통합 테스트로 상태 코드와 헤더를 검증합니다.

꼬리 질문

  • 정적 파일과 인증 순서는 어떻게 결정합니까?
  • 단락 응답에도 추적 ID를 넣으려면?

위험한 답변

미들웨어는 모두 독립적이어서 등록 순서는 성능에만 영향을 준다는 답변입니다.

먼저 생각하기

TLS, 라우팅, 보호와 신뢰 경계를 구분합니다.

모범 답변

리버스 프록시는 TLS 종료, 호스트·경로 라우팅, 연결 관리, 정적 응답과 엣지 보호를 중앙화할 수 있고 Kestrel은 애플리케이션 HTTP 처리에 집중합니다. 필수 구조는 아니지만 운영 경계를 단순화합니다. 대신 전달 헤더는 알려진 프록시만 신뢰하고 본문 크기·타임아웃·원본 IP 정책을 두 계층에서 일치시켜야 합니다.

꼬리 질문

  • Kestrel을 직접 인터넷에 공개할 수 있습니까?
  • Forwarded Headers 오구성의 보안 영향은?

위험한 답변

Kestrel은 외부 요청을 받을 수 없으므로 항상 Nginx가 필수라는 답변입니다.

먼저 생각하기

주체 확인과 자원 행동 판단을 분리합니다.

모범 답변

인증은 토큰 등으로 호출 주체를 확인하고, 인가는 그 주체가 특정 자원에서 행동할 권한이 있는지 판단합니다. 정책은 역할 문자열만 보지 않고 claim, 계정 상태, 자원 소유권 같은 requirement와 handler로 표현합니다. 엔드포인트 기본 거부를 적용하고 플레이어 ID를 클라이언트 입력만으로 신뢰하지 않으며 허용·거부 감사 근거를 남깁니다.

꼬리 질문

  • 리소스 기반 인가는 언제 필요합니까?
  • 토큰 폐기와 권한 변경 전파는 어떻게 합니까?

위험한 답변

로그인에 성공하면 모든 API 접근이 허용되며 역할 하나면 충분하다는 답변입니다.

먼저 생각하기

애플리케이션 도달 전 링크·프록시 포화를 봅니다.

모범 답변

애플리케이션 Rate Limiting은 로그인·쿠폰 같은 고비용 경로의 공정성과 자원 고갈을 완화하지만 요청이 서버에 도달하기 전 회선이나 프록시가 포화되는 대규모 공격은 막지 못합니다. CDN·WAF·네트워크 공급자 보호와 함께 계정·IP·기기별 정책을 조합하고, 우회와 NAT 오탐을 고려해 429·재시도 계약과 거부율을 관측합니다.

꼬리 질문

  • IP 기준 제한이 모바일 통신망에서 위험한 이유는?
  • 분산 인스턴스의 제한 상태를 어디에 둡니까?

위험한 답변

API에 고정 IP 제한 하나를 추가하면 모든 DDoS와 계정 남용이 해결된다는 답변입니다.

먼저 생각하기

변경 저장, identity resolution, 조회 비용을 봅니다.

모범 답변

조회한 엔티티를 같은 DbContext에서 수정해 SaveChanges할 때는 추적이 원본 값과 변경 감지를 제공합니다. 읽기 전용 API는 AsNoTracking과 DTO projection으로 추적 비용과 불필요한 열을 줄입니다. 중복 엔티티 identity resolution이 중요한 그래프는 대안을 비교하고, 생성 SQL·할당·응답 지연을 실제 데이터로 측정합니다.

꼬리 질문

  • AsNoTrackingWithIdentityResolution은 언제 씁니까?
  • projection과 Include의 선택 기준은?

위험한 답변

AsNoTracking은 항상 더 빠르므로 모든 쿼리에 전역 적용해야 한다는 답변입니다.

먼저 생각하기

요청당 SQL 횟수와 필요한 데이터 모양을 봅니다.

모범 답변

N+1은 목록 한 번 뒤 각 행의 연관 데이터를 추가 조회해 왕복 수가 데이터 크기에 비례하는 문제입니다. 개발·부하 환경의 EF 명령 로그와 trace에서 동일 모양 SQL 반복과 요청당 쿼리 수를 확인합니다. 필요한 필드 projection, 적절한 Include, 명시적 일괄 조회로 제거하고 큰 조인 중복은 split query와 왕복 비용을 비교해 결정합니다.

꼬리 질문

  • lazy loading이 문제를 숨기는 방식은?
  • Include를 많이 쓰면 어떤 문제가 생깁니까?

위험한 답변

모든 관계를 한 번에 Include하면 쿼리 수와 데이터 크기에 관계없이 항상 최적이라는 답변입니다.

먼저 생각하기

한 번의 저장과 여러 저장·외부 시스템을 나눕니다.

모범 답변

지원되는 관계형 공급자에서 한 번의 SaveChanges 변경은 기본적으로 트랜잭션으로 적용되어 전부 성공하거나 롤백됩니다. 여러 SaveChanges와 조회를 하나의 원자 업무로 묶어야 할 때만 명시적 트랜잭션을 검토합니다. 원격 API나 메시지 전송을 DB 트랜잭션 안에 넣지 않고 outbox를 쓰며, 실행 전략 재시도와 수동 트랜잭션의 호환성도 확인합니다.

꼬리 질문

  • savepoint는 어떤 충돌 복구에 유용합니까?
  • 격리 수준을 높일 때 생기는 비용은?

위험한 답변

SaveChanges는 트랜잭션이 아니므로 모든 요청을 수동 트랜잭션으로 감싸야 한다는 답변입니다.

먼저 생각하기

충돌 감지 뒤 업무 규칙 재검증을 강조합니다.

모범 답변

잔액 행에 rowversion 같은 동시성 토큰을 두면 갱신 조건이 읽은 버전을 포함하고 불일치 시 DbUpdateConcurrencyException이 발생합니다. 충돌하면 최신 잔액을 다시 읽어 차감 가능 여부를 재검증하고 제한 횟수만 재시도합니다. 네트워크 중복은 별개이므로 명령 ID와 원장 유일 제약을 함께 두고 충돌률이 높으면 플레이어별 직렬화를 검토합니다.

꼬리 질문

  • 재시도할 때 기존 엔티티 상태를 어떻게 갱신합니까?
  • 비관적 잠금으로 전환할 기준은?

위험한 답변

충돌 예외가 나면 같은 DbContext로 무한 재시도하면 항상 안전하다는 답변입니다.

먼저 생각하기

원본, 허용 stale, 무효화 실패를 정의합니다.

모범 답변

DB를 권위 원본으로 두고 Redis와 프로세스 로컬 캐시는 데이터별 허용 stale 시간을 명시한 cache-aside 계층으로 사용합니다. 쓰기 뒤 버전 키나 무효화 이벤트로 두 계층을 갱신하되 메시지 유실을 대비해 TTL을 둡니다. 인기 키는 single-flight와 만료 jitter로 스탬피드를 막고, 재화 잔액처럼 강한 일관성이 필요한 값은 캐시 응답만으로 확정하지 않습니다.

꼬리 질문

  • 무효화 이벤트 순서가 뒤바뀌면 어떻게 합니까?
  • Redis 장애 때 로컬 stale을 얼마나 허용합니까?

위험한 답변

TTL을 길게 두면 모든 캐시가 자동으로 같은 값을 가지며 Redis를 원본처럼 써도 된다는 답변입니다.

먼저 생각하기

증상 범위, 자원 원인, 단일 요청 경로를 잇습니다.

모범 답변

메트릭의 요청률·오류·지연으로 영향 시간과 엔드포인트를 좁히고 CPU·큐·연결 풀 포화와 배포 버전을 겹쳐 봅니다. 느리거나 실패한 trace 표본에서 의존 서비스 span을 찾고 같은 trace ID의 구조화 로그로 업무 상태를 확인합니다. 고카디널리티 값과 개인정보는 label에 넣지 않고 샘플링·보존 정책을 정해 세 신호의 시간을 동기화합니다.

꼬리 질문

  • trace가 샘플되지 않은 오류는 어떻게 조사합니까?
  • 플레이어 ID를 안전하게 검색하려면?

위험한 답변

모든 요청 본문과 모든 trace를 영구 저장하면 어떤 장애든 해결할 수 있다는 답변입니다.

먼저 생각하기

업무 명령 ID와 DB 원자 경계를 둡니다.

모범 답변

클라이언트나 상위 서비스가 안정적인 command ID를 보내고 서버는 플레이어·명령 유형과 함께 멱등 기록에 DB 유일 제약을 둡니다. 같은 트랜잭션에서 명령 결과, 불변 재화 원장, 잔액을 저장해 첫 요청만 효과를 냅니다. 응답이 유실되어 재시도되면 저장된 결과를 반환하고, 키 만료 정책과 다른 payload로 같은 키가 온 경우의 거부 규칙을 명시합니다.

꼬리 질문

  • 멱등 키 저장소 장애 시 지급을 허용합니까?
  • 서로 다른 서버가 동시에 처리하면 무엇이 막습니까?

위험한 답변

메모리 HashSet으로 최근 요청만 기억하거나 클라이언트가 재시도하지 않게 하면 충분하다는 답변입니다.

먼저 생각하기

처리 결과 커밋과 ack 사이의 실패를 설명합니다.

모범 답변

at-least-once에서는 업무 커밋 뒤 ack 전에 소비자가 죽으면 같은 메시지가 다시 옵니다. 메시지 ID나 업무 키를 inbox 테이블의 유일 제약으로 기록하고 재화 변경과 같은 DB 트랜잭션에 묶어 첫 처리만 효과를 내게 합니다. 외부 부수 효과도 자체 멱등 키나 outbox로 분리하고, poison message는 제한 재시도 후 격리하되 원인과 재처리 도구를 남깁니다.

꼬리 질문

  • inbox 기록은 언제 정리할 수 있습니까?
  • 메시지 순서와 중복 제거는 어떤 관계입니까?

위험한 답변

브로커의 exactly-once 옵션을 켜면 DB와 외부 API까지 중복 효과가 사라진다는 답변입니다.

먼저 생각하기

DB 상태와 메시지 발행의 이중 쓰기를 봅니다.

모범 답변

Outbox는 업무 변경과 발행할 이벤트를 같은 DB 트랜잭션에 저장해 DB는 성공했지만 메시지 발행이 유실되는 이중 쓰기 문제를 줄입니다. 별도 발행기가 미전송 행을 재시도하고 성공 상태를 기록합니다. 그러나 발행은 중복될 수 있어 소비자 멱등성이 필요하며, 순서·적체·보존·스키마 진화와 DB 부하를 별도로 운영해야 합니다.

꼬리 질문

  • polling과 CDC의 선택 기준은?
  • 이벤트 순서를 파티션에서 어떻게 유지합니까?

위험한 답변

Outbox를 쓰면 분산 트랜잭션과 중복 소비, 이벤트 순서 문제가 모두 자동 해결된다는 답변입니다.

먼저 생각하기

입구 제어부터 DB 안전 처리량까지 연결합니다.

모범 답변

엣지 Rate Limiting과 용량 제한 대기열에서 유입을 DB의 검증된 안전 처리량 이하로 평탄화하고 예상 대기·jitter 재시도를 반환합니다. 인증 확인과 토큰 발급 경로를 분리하며 로그인 시 불필요한 쓰기와 캐시 동시 만료를 줄입니다. 큐 길이, 성공률, 연결 풀, DB CPU와 p99에 따라 허용률을 자동 조절하고 의존 장애 때 신규 유입을 먼저 줄입니다.

꼬리 질문

  • 대기열의 공정성과 우선순위를 어떻게 정합니까?
  • 오토스케일만으로 해결되지 않는 이유는?

위험한 답변

API 인스턴스를 무제한 늘리고 모든 실패를 즉시 재시도하면 DB도 함께 확장된다는 답변입니다.

먼저 생각하기

불변 감사 기록과 빠른 현재 상태를 나눕니다.

모범 답변

재화 원장은 player ID, currency, delta, reason, command ID, 발생 시각을 가진 append-only 행으로 두고 command ID에 유일 제약을 둡니다. 현재 잔액과 아이템 수량은 같은 트랜잭션에서 갱신하는 구체화 상태로 유지하며 버전 열과 음수 금지 규칙을 적용합니다. 정기 대조로 원장 합계와 상태를 검증하고 수정도 반대 원장으로 남깁니다.

꼬리 질문

  • 장비 인스턴스와 수량형 아이템은 어떻게 다릅니까?
  • 원장 테이블 파티션 키는 무엇으로 정합니까?

위험한 답변

잔액 열 하나만 덮어쓰고 문제가 생기면 최근 로그에서 이유를 추측하면 된다는 답변입니다.

먼저 생각하기

온라인 조회와 감사 가능한 마감을 분리합니다.

모범 답변

실시간 점수와 순위 조회는 모드별 Redis Sorted Set으로 제공하고 모든 점수 변경은 이벤트 ID와 함께 영속 원장에 기록합니다. 시즌 종료 시 cutoff와 동점 규칙을 고정한 버전 스냅샷을 만들고 그 불변 결과에서 보상 명령을 발행합니다. Redis 장애는 최근 스냅샷으로 강등하고 원장에서 재구축하며 지연 이벤트의 포함 여부를 서버 발생 기준으로 명시합니다.

꼬리 질문

  • 동점자의 안정적인 순서를 어떻게 정합니까?
  • 마감 스냅샷 중 쓰기는 어떻게 처리합니까?

위험한 답변

시즌 종료 순간 Redis 상위 목록을 한 번 읽어 바로 보상하면 항상 정확하다는 답변입니다.

먼저 생각하기

서버 검증, 거래 유일성, 보상 분리를 둡니다.

모범 답변

클라이언트 영수증은 스토어 서버에서 검증하고 원거래 ID·상품·계정 매핑을 확인해 거래 ID 유일 제약으로 저장합니다. 검증 결과와 보상 명령 outbox를 같은 트랜잭션에 넣고 지급 소비자는 거래 ID로 멱등하게 원장을 기록합니다. 타임아웃은 실패로 확정하지 않고 jitter를 둔 제한 재시도를 하며 보류·환불은 명시적 상태 전이와 보상 원장으로 처리합니다.

꼬리 질문

  • 결제 계정과 게임 계정 불일치는 어떻게 처리합니까?
  • 수동 재처리 도구에 어떤 안전장치가 필요합니까?

위험한 답변

클라이언트의 결제 성공 콜백을 믿고 먼저 지급한 뒤 중복은 운영자가 정리한다는 답변입니다.

먼저 생각하기

불변 버전, 검증, 단계 출시, kill switch를 둡니다.

모범 답변

설정은 스키마와 의미 검증을 통과한 불변 버전으로 발행하고 요청 한 건은 같은 버전을 사용합니다. 내부 계정, 소수 리전, 전체 순서로 피처 플래그를 확대하며 버전별 오류율·참여율·재화 발행량을 비교합니다. 임계치 초과 시 kill switch로 신규 참여를 막고 이전 버전 포인터로 롤백하되 이미 발생한 보상은 감사 기록과 별도 보상 정책으로 처리합니다.

꼬리 질문

  • 설정 캐시 전파 지연을 어떻게 견딥니까?
  • 서버와 구버전 클라이언트 호환은 어떻게 검증합니까?

위험한 답변

설정 파일을 모든 서버에 덮어쓰고 문제가 나면 프로세스를 재시작해 되돌린다는 답변입니다.

먼저 생각하기

단일 쓰기 소유권과 장애 시 강등을 선택합니다.

모범 답변

플레이어마다 홈 리전을 지정해 권위 상태 쓰기는 한 리전만 소유하고 다른 리전 요청은 홈으로 라우팅합니다. 소유권 epoch를 쓰기 조건에 포함해 이전 리전의 늦은 요청을 거부하며 읽기 모델과 복구 사본만 비동기 복제합니다. 네트워크 분할 때 양쪽 쓰기를 열지 않고 읽기 전용이나 제한 기능으로 강등하고, RPO·RTO에 맞춘 명시적 승격 절차를 실행합니다.

꼬리 질문

  • 플레이어 홈 리전 이전 절차는?
  • 리전 간 길드 거래는 어디서 소유합니까?

위험한 답변

모든 리전에서 자유롭게 쓰고 최종 타임스탬프가 큰 값을 남기면 재화도 안전하다는 답변입니다.

먼저 생각하기

기능별 필수성, stale 허용, 실패 예산을 정합니다.

모범 답변

사용자 여정을 필수 쓰기, stale 허용 읽기, 비필수 장식 기능으로 나누고 각 의존성 실패 계약을 정합니다. 짧은 타임아웃, 제한 재시도와 jitter, 회로 차단, bulkhead로 장애 전파를 막고 공지·추천은 캐시나 생략으로 강등합니다. 결제·재화처럼 결과가 불명확한 쓰기는 성공으로 꾸미지 않고 보류 상태와 멱등 재처리를 제공하며 강등률을 관측합니다.

꼬리 질문

  • 회로 차단 임계치는 어떻게 정합니까?
  • stale 데이터에 사용자 표시가 필요합니까?

위험한 답변

모든 오류를 빈 성공 응답으로 바꾸거나 모든 의존 호출을 여러 번 즉시 재시도한다는 답변입니다.

먼저 생각하기

I/O 동시성과 CPU·콜백 점유 시간을 구분합니다.

모범 답변

Node.js는 네트워크·DB 대기가 많은 API에서 이벤트 루프가 적은 실행 문맥으로 높은 I/O 동시성을 다루기 좋습니다. 반면 큰 JSON 처리, 압축, 암호화나 복잡한 매칭 계산이 이벤트 루프를 오래 점유하면 모든 요청 꼬리 지연이 커집니다. event loop lag와 Worker Pool 포화를 측정하고 CPU 작업은 worker thread나 별도 서비스·제한 큐로 격리합니다.

꼬리 질문

  • libuv Worker Pool을 쓰는 작업 예시는?
  • 클러스터 확장이 상태 소유권에 미치는 영향은?

위험한 답변

Node.js는 단일 스레드라 경쟁 조건이 없거나 어떤 CPU 작업도 처리할 수 없다는 답변입니다.

먼저 생각하기

이름보다 기본 수명과 전파 비용을 비교합니다.

모범 답변

NestJS Provider는 기본 singleton이고 request scope는 의존 그래프를 따라 요청별 생성 비용을 늘릴 수 있습니다. ASP.NET Core의 Singleton·Scoped·Transient에서 웹 Scoped는 보통 요청 단위입니다. 둘 다 긴 수명이 짧은 수명을 캡처하지 않게 하고 요청 상태가 꼭 필요할 때만 좁은 scope를 사용합니다.

꼬리 질문

  • NestJS durable provider는 어떤 문제를 다룹니까?
  • 두 컨테이너에서 transient disposable은 누가 정리합니까?

위험한 답변

두 프레임워크의 scope 이름만 같으면 생성·전파·폐기 동작도 완전히 같다는 답변입니다.

먼저 생각하기

전체 의존 경로의 블로킹 여부와 팀 역량을 봅니다.

모범 답변

Spring MVC는 Servlet 기반 요청 모델과 블로킹 데이터 접근을 사용하는 업무 API에 적합합니다. WebFlux는 높은 동시성의 스트리밍·비동기 I/O에서 전체 경로가 논블로킹이고 팀이 백프레셔와 reactive 디버깅을 운영할 수 있을 때 선택합니다. 블로킹 JDBC를 그대로 감싼 WebFlux는 이점이 제한되므로 실제 부하와 복잡성 비용을 비교합니다.

꼬리 질문

  • 블로킹 라이브러리를 WebFlux에서 격리하려면?
  • 가상 스레드가 선택에 어떤 영향을 줍니까?

위험한 답변

WebFlux는 MVC의 최신 상위 버전이라 모든 API에서 더 빠르다는 답변입니다.

먼저 생각하기

제품 요구, 팀 역량, 운영 생태계를 함께 봅니다.

모범 답변

C# 숙련자와 .NET 온콜 경험이 있고 계정·결제·인벤토리 같은 I/O API를 강한 타입, 성숙한 DI·관측·EF Core 생태계로 운영한다면 ASP.NET Core가 유리합니다. Unity와 계약을 공유할 수 있지만 그것만으로 선택하지 않습니다. 반대로 팀의 주력 운영 플랫폼과 채용 풀이 JVM이거나 핵심 라이브러리가 다른 생태계에 있고 전환 비용이 이익보다 크면 선택하지 않습니다.

꼬리 질문

  • 결정 행렬의 가중치를 누가 정합니까?
  • 언어 간 DTO 공유의 위험은 무엇입니까?

위험한 답변

벤치마크 1위이거나 Unity가 C#이므로 검토 없이 ASP.NET Core가 정답이라는 답변입니다.

먼저 생각하기

추상화 이름보다 쿼리·변경·운영 가시성을 봅니다.

모범 답변

도메인 변경 추적과 Unit of Work 필요, 타입·스키마 생성 방식, LINQ·JPQL·query builder 표현력, 지연 로딩과 N+1 위험을 비교합니다. 이어 마이그레이션, 트랜잭션·동시성 제어, bulk 작업, 생성 SQL과 실행 계획 가시성, 팀의 디버깅 경험을 봅니다. 프레임워크 기능표보다 실제 핵심 쿼리와 장애 복구를 작은 검증 코드로 재현해 선택합니다.

꼬리 질문

  • 범용 리포지터리가 ORM 차이를 숨길 수 있습니까?
  • 스키마 우선과 코드 우선의 선택 기준은?

위험한 답변

ORM은 SQL을 완전히 숨기므로 언어 취향과 벤치마크 처리량만 비교하면 된다는 답변입니다.

먼저 생각하기

현재 능력, 학습 비용, 장기 채용을 수치화합니다.

모범 답변

기능 적합성 외에 온콜 인원, 프로파일링·배포·보안 패치 경험, 기존 CI와 관측 도구, 채용 가능성, 버스 팩터를 결정 행렬에 가중치로 넣습니다. 후보별 대표 API와 장애 시나리오를 짧게 운영해 개발 속도와 복구 시간을 측정합니다. 익숙함만 고집하지도 유행을 위해 전환하지도 않고 교육·이중 운영·마이그레이션의 총비용과 철회 기준을 합의합니다.

꼬리 질문

  • 팀 선호와 제품 제약이 충돌하면 어떻게 합니까?
  • 새 기술 파일럿의 종료 조건은 무엇입니까?

위험한 답변

가장 뛰어난 개발자 한 명이 선호하는 기술을 택하면 팀 전체가 곧 따라온다는 답변입니다.

먼저 생각하기

지휘, 영향 확인, 완화, 소통을 우선합니다.

모범 답변

먼저 incident commander와 기록·소통 역할을 정하고 사용자 영향, 시작 시각, 지역·기능 범위와 SLO 소진을 확인합니다. 최근 배포 중지·롤백, 기능 플래그 차단, 유입 제한처럼 검증된 가역 조치로 영향을 줄이며 한 번에 한 변경을 타임라인에 남깁니다. 원인 추정 토론보다 복구를 우선하고 정기적으로 상태·다음 판단 시점·고객 메시지를 공유합니다.

꼬리 질문

  • 롤백이 데이터 스키마와 호환되지 않으면?
  • 여러 팀이 동시에 변경하려 할 때 어떻게 통제합니까?

위험한 답변

가장 시니어가 혼자 서버에 접속해 로그를 보며 원인을 찾을 때까지 소통을 미룬다는 답변입니다.

먼저 생각하기

불편함이 아니라 위험과 기회비용으로 표현합니다.

모범 답변

부채를 장애 빈도·보안 위험·변경 리드타임·온콜 시간·개발자 대기처럼 관측 가능한 비용과 연결합니다. 영향도와 발생 가능성, 방치 시 성장률, 상환 비용, 제품 일정의 기회비용을 함께 평가해 후보를 정렬합니다. 큰 재작성보다 경계 테스트와 점진적 교체로 위험을 낮추고 분기별 용량과 담당자, 완료 지표를 제품 담당자와 합의해 결과를 다시 측정합니다.

꼬리 질문

  • 측정하기 어려운 부채는 어떻게 설득합니까?
  • 리라이트 요청을 어떤 조건에서 거절합니까?

위험한 답변

개발자가 보기 싫은 오래된 코드를 모두 최신 프레임워크로 다시 쓰는 것이 최우선이라는 답변입니다.

먼저 생각하기

결함 탐지와 지식 확산을 동시에 설계합니다.

모범 답변

작은 PR과 명시적 체크리스트로 정확성, 보안, 실패 모드, 관측·테스트를 우선하고 취향은 자동 포맷터와 팀 규칙에 맡깁니다. 리뷰 의견은 근거와 위험을 설명하며 차단·제안·질문을 구분하고 중요한 판단은 문서로 승격합니다. 페어링, 장애 리뷰, 순환 담당과 후속 피드백으로 특정 시니어에게 승인이 몰리지 않게 하고 리뷰 지연과 결함 유출을 측정합니다.

꼬리 질문

  • 리뷰 의견 충돌을 어떻게 종결합니까?
  • 주니어의 심리적 안전과 기준을 함께 지키려면?

위험한 답변

시니어가 모든 코드를 직접 고쳐 주거나 스타일 지적 수를 품질 지표로 삼는다는 답변입니다.

먼저 생각하기

당시 맥락, 대안, 검증·철회 조건을 남깁니다.

모범 답변

ADR에 문제와 제약, 고려한 대안, 선택 근거, 예상 결과와 소유자를 짧게 기록하고 관련 코드·지표를 연결합니다. 결정은 영구 진리가 아니라 당시 정보의 최선이므로 트래픽, 팀 역량, 비용, 규제 같은 전제가 바뀌거나 SLO·운영 비용이 합의한 임계치를 넘으면 재검토합니다. 새 ADR로 대체 관계와 마이그레이션·롤백 계획을 남겨 역사와 책임을 보존합니다.

꼬리 질문

  • 어떤 결정은 ADR까지 쓰지 않아도 됩니까?
  • 되돌릴 수 없는 결정은 어떻게 실험합니까?

위험한 답변

한 번 승인된 아키텍처는 일관성을 위해 바꾸지 않거나 회의록만 저장하면 충분하다는 답변입니다.

공식 참고 자료

자료 사용 원칙

Microsoft, Node.js, NestJS, Spring 공식 문서를 기준으로 2026-07-14에 검토했습니다. 성능 수치는 환경에 따라 달라지므로 고정 순위로 사용하지 않고, 면접 답변에서는 전제와 측정 방법을 함께 밝힙니다.