Published:
Updated:

깃허브 인터뷰 레포지토리

Table Full Scan, Index Range Scan에 대해 설명해 주세요.

테이블로의 액세스 방법은 Full ScanRange Scan 두 가지가 있습니다.

  • Full Scan
    • 테이블에 포함된 레코드를 처음부터 끝까지 전부 읽어 들이는 방식입니다.
    • Table Full Scan이라고 합니다.
  • Range Scan
    • 테이블의 일부 레코드에만 액세스하는 방식입니다.
    • Index Range Scan이라고 합니다.

구현에 따라 다소 변형의 차이가 있지만, 관계형 데이터베이스에서 테이블로의 액세스 방법은 기본적으로 2가지 패턴밖에 없습니다.

  • 이 2가지 패턴은 테이블의 책으로 생각하면 이해하기 쉽습니다.
    • Full Scan: 처음부터 마지막 페이지까지 1페이지씩 넘기면서 모조리 읽는 방법
    • Range Scan: 색인(인덱스)에서 찾는 단어가 있는 페이지만을 선택해 읽는 방법

Screenshot 2024-10-29 at 16 25 33

가끔은 인덱스를 타는 쿼리임에도 Table Full Scan 방식으로 동작하는 경우가 있습니다. 왜 그럴까요?

  • 데이터 분포도 기반 판단
    • 조회하려는 데이터가 전체 테이블의 20-25% 이상을 차지할 경우, 옵티마이저는 인덱스 스캔보다 Full Scan이 더 효율적이라고 판단합니다.
    • IN 연산자 사용 시 포함된 데이터의 비율이 높다면 Table Full Scan을 선택합니다.
  • 인덱스 사용이 비효율적인 경우
    • 인덱스 컬럼을 변형하여 사용하는 경우 (예: TO_CHAR(column_name))
    • LIKE 검색 시 와일드카드(%)가 앞에 위치하는 경우 (LIKE '%값')
    • 복합 인덱스의 순서를 올바르게 사용하지 않은 경우
  • 시스템 환경 요인
    • 테이블의 크기가 매우 작아서 DB_FILE_MULTIBLOCK_READ_COUNT 이내의 블록으로 구성된 경우
    • 병렬 처리가 필요한 경우 (병렬처리는 Full Table Scan이 더 효율적)

COUNT (개수를 세는 쿼리) 는 어떻게 동작하나요? COUNT(1), COUNT(*), COUNT(column) 의 동작 과정에는 차이가 있나요?

COUNT는 기본적으로 행의 개수를 세는 집계 함수입니다.

COUNT(*) 동작 방식

  • 테이블의 모든 행을 카운트하며, NULL 값도 포함합니다.
  • 실제로 데이터를 읽지 않고 레코드 수만 불러오기 때문에 가장 빠른 성능을 보입니다.
  • 옵티마이저가 가장 효율적인 Execution Plan을 가질 수 있습니다.
  • 일반적인 행 카운트는 COUNT(*)를 사용하는 것이 가장 효율적입니다.

COUNT(1) 동작 방식

  • COUNT(*)와 동일한 방식으로 동작합니다.
  • 쿼리 결과의 모든 레코드를 1로 대체한 후 카운트합니다.
  • 성능상 COUNT(*)와 차이가 없습니다.
  • 그렇기 때문에 사실상 COUNT(*)을 사용하지 않고 COUNT(1)을 사용할 이점은 없습니다.

COUNT(column) 동작 방식

  • 지정된 컬럼의 NULL이 아닌 값만 카운트합니다.
  • 실제 데이터를 읽어야 하므로 COUNT(*)보다 성능이 떨어집니다.
  • NULL 체크 로직이 추가되어 처리 과정이 더 복잡합니다.
  • NULL을 제외한 카운트가 필요한 경우에만 COUNT(column)을 사용해야 합니다

SQL Injection에 대해 설명해 주세요.

SQL Injection은 웹 애플리케이션의 보안 취약점을 이용하여 악의적인 SQL 쿼리를 주입하는 공격 기법입니다. 데이터베이스의 중요 정보를 탈취하거나 조작할 수 있어 매우 위험합니다.

주요 공격 유형

  1. Error based SQL Injection
    • 데이터베이스 에러를 유발시켜 정보를 획득하는 방식입니다.
  2. Union based SQL Injection
    • SQL의 UNION 연산자를 사용하여 원래 쿼리의 결과에 추가 데이터를 결합 후 임의의 테이블 정보를 조회하는 방식입니다.
    • 두 쿼리의 컬럼 수와 데이터 타입이 일치해야 합니다.
  3. Blind SQL Injection
    • 데이터베이스의 true/false 응답만으로 정보를 유추하는 방식입니다.
    • 직접적인 데이터 노출이 없을 때 사용됩니다.

방어 전략

  1. 입력값 검증
    • 사용자 입력에 대한 철저한 검증을 수행해야 합니다.
    • 특수문자 및 SQL 예약어를 필터링합니다.
  2. Prepared Statement 사용
    • 쿼리문과 파라미터를 분리하여 처리합니다.
      • ex) 우리가 코드 짤 때 항상 하는 파라미터를 바인딩하는 것을 생각하면 됨
    • SQL Injection을 원천적으로 방지할 수 있습니다.
  3. 에러 메시지 제한
    • 데이터베이스 에러 메시지를 사용자에게 직접 노출하지 않습니다.
    • 공격자가 데이터베이스 구조를 파악하지 못하도록 합니다.

그렇다면, 우리가 서버 개발 과정에서 사용하는 수많은 DB 라이브러리들은 이 문제를 어떻게 해결할까요?

ORM 프레임워크를 예로 들어 보겠습니다.

방어 전략

  • Prepared Statement를 자동으로 사용합니다.
  • 특수 문자를 자동으로 이스케이프 처리합니다.
    • ex) “\“를 특수 문자 앞에 붙여서 해당 문자를 일반 문자로 취급하도록 만듦
  • 타입 캐스팅을 강제하여 입력값을 검증합니다.
    • ex) 문자열 형태의 “2024-10-29”가 들어왔을 때 이걸 날짜 타입으로 변환

주의사항

  • 동적 쿼리 생성 시에는 여전히 SQL Injection 위험이 있습니다.
  • ORM만으로는 완벽한 방어가 어려우므로 추가적인 보안 계층이 필요합니다.
    • 추가적인 보안 계층
      • 입력값 검증
        • 서버 사이드에서 입력값 검증을 수행해야 합니다.
        • 허용된 입력만 받아들이는 화이트리스트 방식의 검증이 권장됩니다.
      • 데이터베이스 권한 제한
        • 애플리케이션에서 사용하는 DB 계정의 권한을 최소화합니다.
        • 필요한 테이블과 작업에 대해서만 접근 권한을 부여합니다.

Leave a comment