courses
거의 모든 데이터베이스 관련 채용 공고에 MySQL이 포함되어 있다는 점, 눈치채셨나요? 그럴 만한 이유가 있습니다 — MySQL은 우리가 매일 사용하는 앱부터 즐겨 쓰는 소셜 미디어 플랫폼까지 사실상 대부분을 구동합니다.
이 가이드는 MySQL 면접 질문을 효과적으로 준비할 수 있도록 구성했습니다. 주니어 개발자가 알아야 할 기초부터 시니어 직무에 필요한 심화 주제까지 모두 다룹니다. 또한 다음 데이터 관련 면접에서 자신감 있게 임할 수 있도록 몇 가지 팁도 함께 공유합니다.
MySQL이란?
MySQL은 SQL 위에 구축된 오픈 소스 RDBMS(관계형 데이터베이스 관리 시스템)로, 데이터를 구조화된 테이블에 저장하고 관리합니다. Oracle Corporation에서 개발했습니다.
2024년에 가장 인기 있는 DBMS로 선정되었습니다. 다만, 2025년 Stack Overflow 개발자 설문에서는 PostgreSQL이 전문 개발자 사이에서 가장 널리 사용되는 데이터베이스로 처음으로 MySQL을 앞질렀습니다.
그렇다고 해서 오해하진 마세요. MySQL은 여전히 엄청나게 널리 쓰이며 — 2025년 개발자 사용률 40.5%에 달했고 — 수많은 웹 애플리케이션, 콘텐츠 관리 시스템, 엔터프라이즈 도구의 핵심입니다. 특히 웹 애플리케이션이나 LAMP 스택을 다룬다면 MySQL은 최고의 핵심 역량입니다.

2024년, MySQL은 전 세계에서 가장 인기 있는 오픈 소스 DBMS로, 순위 점수 1061을 기록했습니다. 출처: Statista.
기초 MySQL 면접 질문
초기 면접 단계에서는 기본적인 데이터베이스 및 MySQL 개념에 대한 이해를 평가하기 위한 질문이 나올 수 있습니다.
1. 데이터베이스란 무엇이며, DBMS와는 어떻게 다른가요?
데이터베이스는 데이터를 저장해 두고 접근, 수정, 분석할 수 있는 저장소입니다. 예를 들어 소셜 미디어 플랫폼은 누가 우리 게시글에 좋아요를 눌렀는지에 대한 데이터를 데이터베이스에 저장합니다.
DBMS(Database Management System)는 사용자 생성과 접근 제어 등으로 해당 데이터를 관리하고 상호작용하게 해 주는 소프트웨어입니다. MySQL은 가장 인기 있는 DBMS 중 하나입니다. 다른 예로는 PostgreSQL, MongoDB, Microsoft SQL Server가 있습니다.
2. MySQL은 다른 관계형 DBMS와 무엇이 다른가요?
MySQL은 SQL을 사용해 데이터를 관리하는 오픈 소스 관계형 데이터베이스 관리 시스템(RDBMS)입니다. 사용이 쉽고 빠르며 웹 기반 애플리케이션과의 호환성이 뛰어난 것으로 알려져 있습니다.
MySQL이 다른 RDBMS와 다른 점은 다음과 같습니다.
- 단순성과 성능: MySQL은 단순성과 최적화된 성능으로 자주 호평받으며, 웹 개발자와 스타트업의 대표적인 선택지입니다.
- 고급 기능: MySQL은 사용성이 뛰어나지만, PostgreSQL처럼 보다 포괄적인 ACID 트랜잭션 지원, 고급 인덱싱, 더 폭넓은 데이터 타입 등 일부 고급 기능이 상대적으로 부족할 수 있습니다.
- 스토리지 엔진: MySQL은 테이블별로 서로 다른 스토리지 엔진(예: InnoDB, MyISAM)을 선택할 수 있어, 용도에 맞게 유연하게 구성할 수 있습니다.
MySQL은 속도와 확장성이 중요한 시나리오에 적합하며, 보다 복잡하거나 엔터프라이즈급 기능이 필요하다면 PostgreSQL이 더 나은 선택일 수 있습니다.
3. MySQL에서 사용할 수 있는 주요 데이터 타입은 무엇인가요?
MySQL은 다음과 같이 분류되는 다양한 데이터 타입을 지원합니다.
-
숫자형:
INT,DECIMAL,FLOAT,DOUBLE등 -
문자열:
CHAR,VARCHAR,TEXT,BLOB -
날짜/시간:
DATE,DATETIME,TIMESTAMP,TIME -
JSON: JSON 객체 저장용
4. INT와 DECIMAL 데이터 타입의 차이는 무엇인가요?
INT는 소수점이 없는 정수를 저장합니다. 분수가 필요 없을 때 사용합니다. 반대로 DECIMAL은 금액처럼 소수점 이하가 필요한 값을 정밀하게 저장하는 데 적합합니다.
5. MySQL에서 DATE와 DATETIME의 차이는 무엇인가요?
DATE는 연, 월, 일 형식으로 날짜를 저장합니다:
YYYY-MM-DD
반면 DATETIME은 날짜와 시간을 함께 저장하며, 형식은 다음과 같습니다:
YYYY-MM-DD HH:MM:SS
6. 외래 키란 무엇이며, 데이터베이스에서 어떻게 사용하나요?
외래 키는 한 테이블의 필드로서, 다른 테이블의 기본 키와 연결됩니다.
예를 들어 고객 정보를 저장하는 customers 테이블에서 각 고객은 고유한 customer_id를 가집니다. 구매 내역을 저장하는 transactions 테이블에서는 customer_id를 외래 키로 사용합니다. 즉, transactions 테이블의 customer_id는 각 구매를 customers 테이블의 특정 고객과 연결합니다.
SQL 예시는 다음과 같습니다.
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100)
);
CREATE TABLE transactions (
transaction_id INT PRIMARY KEY,
customer_id INT,
amount DECIMAL(10,2),
date DATE,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
7. INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN의 차이는 무엇인가요?
조인은 관련 있는 컬럼을 기준으로 두 개 이상의 테이블에서 행을 결합합니다. 차이는 다음과 같습니다.
-
INNER JOIN: 두 테이블 모두에서 일치하는 행만 반환합니다.
-
LEFT JOIN: 왼쪽 테이블의 모든 행과 오른쪽 테이블에서 일치하는 행을 반환합니다. 일치하지 않으면 오른쪽 테이블 컬럼은
NULL이 됩니다. -
RIGHT JOIN:
LEFT JOIN과 유사하게, 오른쪽 테이블의 모든 행과 왼쪽 테이블에서 일치하는 행을 반환합니다. -
FULL JOIN:
LEFT JOIN과RIGHT JOIN의 결과를 합쳐 두 테이블의 불일치 행까지 포함합니다. 참고: MySQL은FULL JOIN문법을 기본 지원하지 않습니다. 동일한 결과가 필요하면LEFT JOIN과RIGHT JOIN을UNION으로 결합하세요.
8. MySQL에서 DELETE, TRUNCATE, DROP의 차이는 무엇인가요?
DELETE, TRUNCATE, DROP은 비슷하게 들리지만 동작은 다릅니다.
DELETE: 조건에 따라 테이블의 행을 삭제합니다. 트랜잭션 내에서는 롤백할 수 있습니다. 예:
DELETE FROM employees WHERE department_id = 5;
TRUNCATE: 테이블의 모든 행을 삭제하지만 테이블 구조는 남깁니다. DELETE보다 빠르며 롤백할 수 없습니다. 예:
TRUNCATE TABLE employees;
DROP: 테이블 구조와 데이터를 완전히 제거하며, 인덱스 같은 의존 객체도 함께 삭제합니다. 예:
DROP TABLE employees;
9. MySQL에서 테이블을 생성하고 수정하는 방법은? 예시를 들어 주세요.
테이블 생성에는 CREATE TABLE 문을, 수정에는 주로 ALTER TABLE을 사용합니다. 예시는 다음과 같습니다.
테이블 생성:
CREATE TABLE employees (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), department VARCHAR(50));
컬럼 추가 수정:
ALTER TABLE employees ADD COLUMN salary DECIMAL(10, 2);
10. SQL의 임시 테이블이란?
임시 테이블은 현재 데이터베이스 세션 동안만 존재합니다. 세션이 종료되면 테이블이 삭제됩니다. 중간 결과를 임시로 저장하는 데 유용하며, 테스트, 필터링, 영구 테이블로 삽입하기 전 데이터 준비에 사용할 수 있습니다.
예시는 다음과 같습니다.
CREATE TEMPORARY TABLE temp_employees (
id INT,
name VARCHAR(50)
);
INSERT INTO temp_employees VALUES (1, 'John Doe');
SELECT * FROM temp_employees;
11. MySQL에서 서브쿼리란? 예시로 설명해 주세요.
서브쿼리(중첩 쿼리)는 다른 쿼리 내부에 포함된 쿼리입니다. 복잡한 작업을 더 작은 단계로 나누는 데 도움이 됩니다. 예를 들어 평균 급여보다 더 받는 직원을 찾는 서브쿼리는 다음과 같습니다.
SELECT first_name, last_name, salary
FROM employees
WHERE salary > (
SELECT AVG(salary)
FROM employees
);
설명을 덧붙이면:
-
내부 쿼리
SELECT AVG(salary) FROM employees가 먼저 평균 급여를 계산합니다. -
외부 쿼리는 이 평균을 이용해 그보다 더 받는 직원을 찾습니다.
12. MySQL에서 INSERT 문으로 데이터를 추가하는 방법과 모범 사례는?
INSERT 문으로 테이블에 데이터를 추가할 수 있습니다. 기본 문법은 다음과 같습니다.
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...);
다음은 INSERT 문 사용 시 권장 사항입니다.
-
컬럼을 명시적으로 나열하세요. 코드 가독성이 좋아지고, 테이블 구조가 바뀌어도 오류를 줄일 수 있습니다.
-
AUTO_INCREMENT컬럼(예: ID)은INSERT에서 생략하세요. MySQL이 자동으로 처리해 중복 ID를 방지합니다. -
문자열 따옴표는 일관되게 사용하세요. 필자는 홑따옴표를 선호하지만 어느 쪽도 가능합니다.
-
여러 행을 삽입할 때는 단일 문으로 묶는 것이 성능에 유리합니다.
13. MySQL에서 AUTO_INCREMENT 속성의 의미는 무엇인가요?
AUTO_INCREMENT는 보통 테이블의 기본 키 컬럼에 대해 고유하고 연속적인 숫자를 자동 생성합니다.
다음은 AUTO_INCREMENT 컬럼이 있는 테이블 생성 예시입니다.
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
department VARCHAR(50)
);
행 삽입은 다음과 같습니다.
INSERT INTO employees (name, department) VALUES ('John Doe', 'Sales');
INSERT INTO employees (name, department) VALUES ('Jane Smith', 'Marketing');
14. MySQL에서 뷰(View)란?
뷰는 저장된 쿼리로, 가상 테이블처럼 동작합니다. 복잡한 쿼리에 이름을 부여해 이후 테이블처럼 재사용할 수 있어, 매번 전체 쿼리를 다시 작성할 필요가 없습니다.
예를 들어 직원 정보와 부서명을 함께 조회하는 작업을 단순화하려면 다음과 같이 뷰를 생성할 수 있습니다.
CREATE VIEW employee_details AS
SELECT
e.id,
e.name,
d.department_name,
e.salary
FROM
employees e
JOIN
departments d ON e.department_id = d.department_id;
이제 employee_details 뷰를 테이블처럼 조회할 수 있습니다.
SELECT * FROM employee_details;
다만, 뷰를 통해 삽입이나 업데이트를 수행할 수 없는 경우가 많습니다. 대부분 읽기 전용을 지원하여 사용자의 직접적인 데이터베이스 접근을 제한하고 보안을 강화합니다. 또한 뷰는 접근할 때마다 기반 쿼리를 실행하므로, 경우에 따라 쿼리가 느려질 수 있습니다.
중급 MySQL 면접 질문
이 섹션에서는 중급 수준의 주제를 다룹니다. 주로 MySQL 데이터 타입과 구조에 대한 지식을 평가하는 질문입니다.
15. 시스템 버전 테이블이란 무엇이며, 어떻게 동작하나요?
시스템 버전 테이블은 테이블에 가해진 변경 이력을 모두 유지합니다. 각 행의 과거 버전을 보관하므로 감사를 하거나 데이터를 복구하는 데 사용할 수 있습니다.
각 행이 유효한 기간을 기록하기 위해 StartTime과 EndTime이라는 두 개의 추가 컬럼을 둡니다. 삽입, 업데이트, 삭제 시 타임스탬프가 다음과 같이 갱신됩니다.
-
Insert: 새 행이 추가되며
StartTime은 현재 타임스탬프로,EndTime은9999-12-31 23:59:59로 설정됩니다. 이는 MySQL의 최대DATETIME값으로, ‘현재 활성’ 상태를 나타내는 센티넬입니다. -
Update: 원본 행의
EndTime을 현재 타임스탬프로 바꿔 더 이상 유효하지 않음을 표시하고, 수정된 데이터를 가진 새 행을 현재StartTime과 ‘영구’EndTime으로 생성합니다. -
Delete: 기존 행의
EndTime을 현재 타임스탬프로 갱신해 더 이상 유효하지 않음을 나타냅니다.
SQL의 FOR SYSTEM_TIME 절을 사용하면 특정 시점 또는 기간의 테이블 상태를 조회할 수 있습니다. 예:
-
FOR SYSTEM_TIME AS OF '2024-01-01': 2024년 1월 1일 당시 테이블 상태를 조회합니다. -
FOR SYSTEM_TIME BETWEEN '2024-01-01' AND '2024-12-31': 해당 기간 동안 유효했던 모든 행을 보여 줍니다.
16. MySQL 트랜잭션이란 무엇이며, 어떻게 사용하나요?
트랜잭션은 하나의 단위로 실행되는 연산 집합입니다. 모든 연산이 함께 성공하거나 함께 실패하도록 보장해 데이터 무결성을 지킵니다.
사용 예시는 다음과 같습니다.
START TRANSACTION;
UPDATE accounts SET balance = balance - 500 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 500 WHERE account_id = 2;
COMMIT; -- 변경 사항을 영구 반영
-- 또는
ROLLBACK; -- 변경 사항 되돌리기
17. MySQL의 기본 제약조건(Default constraint)이란? 컬럼 기본값은 어떻게 설정하나요?
기본 제약조건은 INSERT 시 명시적 값이 제공되지 않을 때 컬럼에 기본값을 할당합니다. 사용자가 값을 생략하더라도 컬럼이 유효한 상태로 유지됩니다.
다음은 기본값을 지정한 테이블 생성 예입니다.
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
status VARCHAR(10) DEFAULT 'active'
);
이후 status를 지정하지 않고 행을 삽입할 수 있습니다.
INSERT INTO employees (name) VALUES ('John Doe');
이 접근은 중요한 컬럼에서 NULL 또는 잘못된 데이터를 줄이고, 기본값 처리를 쿼리에서 별도로 할 필요를 줄여 단순화해 줍니다.
|
Field |
Type |
Null |
Key |
Default |
Extra |
|
id |
INT |
NO |
PRI |
NULL |
AUTO_INCREMENT |
|
name |
VARCHAR(50) |
YES |
NULL |
||
|
status |
VARCHAR(10) |
YES |
active |
이 명령이 유용한 이유는 다음과 같습니다.
- 쿼리를 작성하기 전에 테이블 스키마를 이해하는 데 도움이 됩니다.
- 낯선 데이터베이스를 다룰 때 특히 디버깅에 유용합니다.
- 기본 키나 기본값 같은 제약조건을 빠르게 파악할 수 있습니다.
18. MySQL에서 CHAR와 VARCHAR의 차이는 무엇인가요?
두 타입 모두 문자열 데이터를 저장하지만, 저장 방식이 다릅니다.
-
CHAR(n)은 항상 정확히n자의 길이로 저장하며, 더 짧으면 공백으로 채웁니다. 고정 길이이므로 국가 코드나 상태 플래그처럼 값의 길이가 일정한 컬럼에서 약간 더 빠릅니다. -
VARCHAR(n)은 입력한 실제 문자만 최대n자까지 저장합니다. 가변 길이 데이터에 더 적은 저장 공간을 쓰지만 길이 정보를 추적하는 약간의 오버헤드가 있습니다.
CREATE TABLE example (
country_code CHAR(2), -- Always 2 chars, e.g. 'US', 'UK'
email VARCHAR(255) -- Variable length up to 255 chars
);
일반적인 기준: 고정 길이 값에는 CHAR를, 그 외에는 VARCHAR를 사용하세요.
19. SQL의 문자열 함수를 사용해 텍스트를 처리하는 방법은?
SQL의 다양한 문자열 함수는 이름 등 텍스트 데이터를 다루는 데 쓰입니다. 예를 들어:
-
LENGTH()는 이름의 문자 수를 보여 줍니다. -
UPPER()와LOWER()는 텍스트를 각각 모두 대문자/소문자로 변환합니다. -
CONCAT()은 이름과 성을 결합합니다. -
SUBSTRING()은 텍스트의 특정 부분을 추출합니다. 예를 들어 생년월일에서 월만 분리할 수 있습니다.
예시 쿼리는 다음과 같습니다.
SELECT
UPPER(first_name) AS upper_name,
CONCAT(first_name, ' ', last_name) AS full_name,
SUBSTRING(birthdate, 6, 2) AS birth_month,
TRIM(last_name) AS trimmed_last_name,
REPLACE(first_name, 'a', '@') AS replaced_name
FROM employees;
이 쿼리는 다음을 수행합니다.
-
이름을 대문자로 변환
-
이름과 성을 결합해 전체 이름 생성
-
birthdate컬럼에서 출생 월 추출 -
성의 공백 제거
-
이름의 모든 "a"를 "@"로 대체
20. 데이터베이스의 특정 행을 SQL로 업데이트하려면?
UPDATE 문과 WHERE 절로 변경 대상 레코드를 지정합니다.
예를 들어, 2010년 영화 “Inception”의 장르를 “Sci-fi”로 바꾸려면 다음과 같이 작성합니다.
UPDATE movies
SET genre = 'Sci-Fi'
WHERE movie_title = 'Inception' AND year = 2010;
여기서 UPDATE movies는 수정할 테이블을 지정하고, WHERE 절은 제목이 “Inception”이며 연도가 “2010”인 행을 대상으로 합니다.
고급 MySQL 면접 질문
고급 질문은 복잡한 MySQL 상황을 다루는 능력과 의사결정 역량을 평가합니다.
21. MySQL의 트리거란? 구현 방법은?
MySQL에서 트리거는 데이터베이스 이벤트가 발생할 때 실행되는 동작 집합입니다. INSERT, UPDATE, DELETE 같은 이벤트 이전 또는 이후에 실행되도록 설정할 수 있습니다.
예를 들어 orders 테이블에 새 주문이 추가될 때마다 order_history 테이블에 로그를 적재하는 트리거를 만들 수 있습니다.
CREATE TRIGGER after_order_insert
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
INSERT INTO order_history (order_id, action, timestamp)
VALUES (NEW.order_id, 'inserted', NOW());
END;
트리거 실행 후 order_history 테이블은 자동으로 갱신됩니다.
|
history_id |
order_id |
action |
timestamp |
|
1 |
1 |
inserted |
2024-12-24 10:00:00 |
|
2 |
2 |
inserted |
2024-12-24 11:00:00 |
22. 인덱스를 추가하면 SQL 쿼리가 왜 빨라지나요?
인덱스가 없으면 특정 항목을 찾기 위해 데이터베이스는 모든 행을 스캔해야 합니다. 인덱스는 일종의 목차처럼 동작해, 관련 행에 바로 접근할 수 있게 합니다. 즉 인덱스를 추가하면 검색 시간이 줄어들어 쿼리가 더 빨라집니다.
인덱스는 일반적으로 B-트리나 해시 테이블과 같은 자료구조로 구현되어, 검색, 조회, 범위 스캔을 효율적으로 수행합니다.
인덱스 생성 예시는 다음과 같습니다.
-- 인덱스 없이:
SELECT * FROM employees WHERE last_name = 'Smith';
-- last_name 컬럼에 인덱스 추가:
CREATE INDEX idx_last_name ON employees(last_name);
-- 인덱스가 있으면 DB는 last_name이 'Smith'인 행을 빠르게 찾을 수 있습니다.
인덱스의 단점도 있습니다. 예를 들어:
-
쓰기 성능 저하:
INSERT,UPDATE,DELETE시 데이터 변경 때마다 인덱스도 갱신해야 하므로 느려질 수 있습니다. -
저장 공간 비용: 인덱스는 추가 저장 공간을 필요로 합니다.
23. SQL 테이블에서 제품의 무게와 가격에는 어떤 데이터 타입을 쓰며, 그 이유는?
무게에는 일반적으로 DECIMAL이 더 안전한 선택입니다. FLOAT나 REAL도 소수를 저장할 수 있지만 부동소수 연산으로 인해 미세한 반올림 오차가 발생할 수 있습니다.
배송 계산이나 재고 관리처럼 정확도가 중요한 제품 무게에는 DECIMAL(8, 3) 같이 소수 셋째 자리까지 정밀하게 제어할 수 있는 타입이 적합합니다. 약간의 오차가 허용되는 경우에만 FLOAT를 고려합니다.
24. 윈도 함수로 SQL에서 중복 행을 찾는 방법은?
다음과 같이 ROW_NUMBER() 윈도 함수를 사용해 중복을 찾을 수 있습니다.
WITH DuplicateCheck AS (
SELECT product_name,
category,
ROW_NUMBER() OVER(
PARTITION BY product_name, category
ORDER BY id
) AS row_num
FROM sales
)
SELECT *
FROM DuplicateCheck
WHERE row_num > 1;
동작 방식은 다음과 같습니다.
1. ROW_NUMBER()가 결과의 각 행에 번호를 부여합니다.
2. PARTITION BY는 product_name과 category별로 그룹화합니다.
3. 각 그룹 내에서 1부터 번호를 매깁니다.
4. row_num이 1보다 크면 중복을 의미합니다.
예를 들어 다음과 같은 레코드가 있다면:
- Product A, Category X, row_num = 1
- Product A, Category X, row_num = 2 (중복)
- Product B, Category Y, row_num = 1
쿼리는 row_num이 1보다 큰 두 번째 행을 보여 줍니다.
25. 매개변수가 있는 저장 프로시저를 MySQL에서 생성/사용하는 방법은? 예시로 설명해 주세요.
저장 프로시저로 복잡한 쿼리를 저장·재사용하면 데이터베이스 작업을 더 효율적이고 유지보수하기 쉽게 만들 수 있습니다. 실용적인 예시로 매개변수 사용 방법을 살펴보겠습니다.
학생 데이터베이스에서 나이를 기준으로 학생을 필터링하는 프로시저를 만든다고 가정해 보겠습니다. 먼저 나이를 입력 파라미터로 받는 간단한 프로시저는 다음과 같습니다.
CREATE PROCEDURE get_student_info(IN age INT)
BEGIN
SELECT * FROM student WHERE student.age = age;
END;
이 프로시저는 원하는 나이를 지정해 CALL로 실행합니다.
CALL get_student_info(21);
출력 파라미터를 사용해 프로시저를 더 정교하게 만들 수도 있습니다. 예를 들어 특정 나이의 학생 수를 세는 프로시저는 다음과 같습니다.
CREATE PROCEDURE count_students_by_age(IN age INT, OUT student_count INT)
BEGIN
SELECT COUNT(*) INTO student_count FROM students WHERE students.age = age;
END;
이 프로시저의 결과를 받으려면 다음과 같이 합니다.
SET @count = 0;
CALL count_students_by_age(21, @count);
SELECT @count AS total_students;
26. 데이터베이스에서 참조 무결성이 중요한 이유는?
참조 무결성은 테이블 간 관계를 정확하게 유지합니다. 외래 키를 생성하면 한 테이블의 값이 참조 대상 테이블의 고유 값과 일치하도록 보장합니다.
실용적인 예시로 전자상거래 데이터베이스를 생각해 봅시다. Customers 테이블과 Orders 테이블이 있고, 모든 주문은 실제 고객에 속해야 합니다. 외래 키를 통한 참조 무결성은 다음을 보장합니다.
- 존재하지 않는 고객에 대한 주문을 생성할 수 없습니다.
- 기존 주문이 있는 고객은 삭제할 수 없습니다(주문에 대한 처리 방식을 별도로 설정하지 않는 한).
- 주문이 있는 고객의 ID를 변경할 수 없습니다.
다음과 같이 외래 키 제약을 추가하면:
ALTER TABLE Orders
ADD FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID);
데이터베이스는 다음 규칙을 자동으로 강제합니다.
-
Orders의 모든CustomerID는Customers에 존재해야 합니다. -
유효하지 않은
CustomerID를 삽입하는 등 규칙을 위반하려는 시도는 거부됩니다.
이를 통해 실제 고객과 연결되지 않는 주문이나, 고객 정보가 누락된 보고서 같은 심각한 불일치를 방지할 수 있습니다.
데이터베이스 관리자 대상 MySQL 면접 질문
DBA 직무에 지원한다면 다음과 같은 질문을 받을 수 있습니다.
27. 대규모 애플리케이션이 샤딩을 사용하는 이유와 그 과제는?
대규모 애플리케이션은 방대한 데이터를 여러 서버로 분산하기 위해 데이터베이스 샤딩을 사용합니다. 각 샤드는 데이터의 일부를 보유합니다. 데이터 부하가 분산되므로 고성능 하드웨어 의존도가 줄고, 속도와 확장성도 향상됩니다. 하지만 다음과 같은 과제가 있습니다.
- 조인 같은 일부 쿼리가 어렵거나 불가능해져 데이터 관리가 복잡해질 수 있습니다.
- 데이터가 증가하면 특정 샤드에 부하가 집중되어 핫스팟이 생기고 성능이 저하될 수 있습니다.
28. MySQL 크래시 복구에서 리두 로그의 역할을 설명해 주세요.
MySQL에서 데이터가 수정될 때마다 디스크에 기록되어야 합니다. 하지만 데이터를 직접 파일에 쓰는 것은 느리고 위험할 수 있습니다. 그래서 MySQL은 데이터 파일을 수정하기 전에 먼저 리두 로그에 변경 내용을 기록합니다. 무작위로 데이터 파일을 업데이트하는 것보다 안전합니다.
예를 들어 고객 주소를 업데이트한다고 가정해 보겠습니다.
- MySQL은 먼저 이 변경을 리두 로그에 기록합니다.
- 그다음 트랜잭션 커밋을 확인합니다.
- 이후 실제 데이터 파일에 변경을 적용합니다.
크래시 복구는 1 또는 2단계 이후 3단계 이전에 MySQL이 중단된 경우 중요합니다. 재시작 시 MySQL은 리두 로그를 확인해 기록된 변경을 재실행하여 미완료 작업을 마무리합니다. 이를 통해 불편한 시점에 장애가 나더라도 커밋된 트랜잭션이 유실되지 않도록 보장합니다.
29. MySQL에서 사용할 수 있는 스토리지 엔진과 차이점은?
MySQL은 여러 스토리지 엔진을 지원하며, 각각 용도에 최적화되어 있습니다. 대표적인 엔진을 비교하면 다음과 같습니다.
| Storage Engine | Key Features | Best For |
|---|---|---|
| InnoDB | 기본 엔진. ACID 준수, 행 단위 잠금, 트랜잭션과 외래 키 지원. | 전자상거래, 금융 시스템 등 데이터 무결성이 중요한 워크로드. |
| MyISAM | 빠른 읽기, 테이블 단위 잠금. 트랜잭션/외래 키 미지원. | 읽기 비중이 높고 무결성보다 속도가 중요한 앱. |
| Memory | RAM에 데이터 저장. 매우 빠르지만 재시작 시 데이터 소실. | 캐싱, 세션 관리, 임시 데이터. |
| CSV | CSV 평문 파일로 저장. 인덱싱 없음. | 애플리케이션 간 데이터 교환 또는 단순 플랫 파일 저장. |
| Archive | 높은 압축률. INSERT와 SELECT만 지원. 인덱스 없음. | 자주 조회되지 않는 로그나 이력 데이터. |
| NDB (Clustered) | 분산 저장, 고가용성, 장애 허용, 트랜잭션 지원. | 실시간 성능이 필요한 대규모 분산 애플리케이션. |
30. MySQL에서 기본 스토리지 엔진을 설정하는 방법은?
먼저 현재 기본 스토리지 엔진을 확인합니다.
SHOW ENGINES;
InnoDB는 다음과 같은 중요한 기능을 지원하므로 기본 엔진으로 권장됩니다.
- ACID 트랜잭션
- 외래 키 제약
- 크래시 복구
- 행 단위 잠금
현재 세션에서 일시적으로 기본 엔진을 바꾸려면 다음을 사용할 수 있습니다.
SET default_storage_engine = 'InnoDB';
영구 변경을 위해서는 MySQL 설정 파일의 [mysqld] 섹션에 다음 줄을 추가합니다.
default-storage-engine = InnoDB
31. 손상된 MySQL 테이블을 복구하는 방법은?
먼저 다음 명령으로 모든 데이터베이스를 검사합니다.
mysqlcheck --check --all-databases -u root -p
모든 테이블을 스캔해 손상 여부를 보고합니다. 이후 다음 명령으로 테이블을 복구할 수 있습니다.
mysqlcheck --repair database_name table_name -u root -p
심각한 손상의 경우 복구 과정에서 데이터가 손실될 수 있으니, 반드시 백업을 수행하세요.
시나리오 기반/문제 해결 MySQL 면접 질문
현실 세계의 복잡한 시나리오 경험과 문제 해결 능력을 평가하는 질문입니다.
32. MySQL에서 서브쿼리를 사용했던 사례를 설명해 주세요.
다음과 같이 답할 수 있습니다.
최근 재직 중인 회사의 이커머스 데이터베이스에서 제품 보고서를 준비했습니다. 목표는 평균을 웃도는 매출을 낸 제품을 찾는 것이었고, 이를 위해 여러 단계를 서브쿼리로 처리했습니다.
이를 해결하기 위해 작성한 SQL은 다음과 같습니다.
SELECT
p.product_id,
p.product_name,
s.sales_amount
FROM products p
JOIN sales s ON p.product_id = s.product_id
WHERE s.sales_amount > (
SELECT AVG(sales_amount)
FROM sales
)
ORDER BY s.sales_amount DESC;
먼저 서브쿼리로 전체 제품의 평균 매출을 계산해 기준선을 만들었습니다. 이 서브쿼리는 동적으로 임계값을 제공해 각 제품의 성과를 비교할 수 있게 했습니다.
메인 쿼리는 제품과 매출 테이블을 조인해 필요한 세부 정보를 가져오고, WHERE 절에서 평균 미만 제품을 제외했습니다.
이 구조로 단일 쿼리만으로 고성과 제품을 식별할 수 있어, 여러 번의 별도 쿼리를 실행할 필요가 없었습니다.
33. 여러 테이블의 데이터를 결합하기 위해 SQL 조인을 사용한 사례를 설명해 주세요.
다음은 예시 답변입니다.
최근 프로젝트에서 제품 매출 데이터 테이블과 제품 세부 정보 테이블, 두 개의 주요 테이블이 있었습니다. 제 과제는 sales, product name, category, price를 보여 주는 보고서를 작성하는 것이었습니다.
관련 데이터를 결합하기 위해 공통 컬럼 product_id로 INNER JOIN을 사용하여 판매 거래와 제품 세부 정보를 연결했습니다.
SELECT
s.sales_date,
p.product_name,
p.category,
s.quantity_sold,
p.price
FROM
sales s
INNER JOIN
products p
ON
s.product_id = p.product_id;
이 보고서는 판매 추세를 한눈에 보여 주었고, 이해관계자가 어느 카테고리가 잘 팔리고 보완이 필요한지 파악하는 데 도움이 되었습니다.
34. 트리거 사용 경험이 있나요? 어떻게 활용했는지 설명해 주세요.
다음은 예시 답변입니다.
네, 트리거를 폭넓게 사용했습니다. 최근 역할에서는 가격 변경 감사를 위해 AFTER UPDATE 트리거를 구현했습니다.
구체적으로는 제품 가격이 변경될 때마다 가격 이력을 자동으로 저장하는 트리거를 만들었습니다. 다음은 제가 작성한 SQL 스크립트입니다.
CREATE TRIGGER tr_AuditPriceChanges
AFTER UPDATE ON Products
FOR EACH ROW
BEGIN
-- Only log if the price actually changed
IF OLD.UnitPrice <> NEW.UnitPrice THEN
INSERT INTO PriceAudit (
ProductID,
OldPrice,
NewPrice,
ChangedBy,
ChangeDate,
PercentageChange
)
VALUES (
NEW.ProductID,
OLD.UnitPrice,
NEW.UnitPrice,
CURRENT_USER(),
NOW(),
ROUND(((NEW.UnitPrice - OLD.UnitPrice) / OLD.UnitPrice * 100), 2)
);
END IF;
END;
이 솔루션이 특히 효과적이었던 이유는 다음과 같습니다.
- 실제 가격 변경이 있을 때만 동작합니다.
SYSTEM_USER를 사용해 변경한 사용자를 기록합니다.- 보고를 위해 변경 비율을 계산합니다.
- 다른 컬럼 업데이트로 인한 ‘변경 없음’ 상황을 걸러내는
WHERE절을 포함합니다.
또한 NULL 가격과 관련된 엣지 케이스를 발견한 후 오류 처리와 로깅을 추가했습니다.
MySQL 면접 준비 팁
커리어를 이제 시작한다면 다음 팁이 면접에 큰 도움이 됩니다.
MySQL 핵심 개념 마스터: 핵심 데이터베이스 기초를 학습하고 인덱싱, 트랜잭션, 쿼리 옵티마이저를 이해하세요. MySQL이 쿼리를 처리하고 데이터를 저장하는 방식을 알면 효율적인 쿼리를 작성하고 면접에서 해결 과정을 잘 설명할 수 있습니다.
실습 위주로 익히기: 내 컴퓨터에 MySQL을 설치하고 꾸준히 연습하세요. 테스트 데이터베이스를 만들고 다양한 쿼리를 작성·최적화해 보세요. 실제로 손을动해 보는 것이 가장 효과적인 학습 방법이며, 면접에서도 자신감을 줍니다.
지식을 보강하려면 DataCamp 자료도 참고하세요:
- SQL 입문 과정: Introduction to SQL Course
- SQL 연습: Applying SQL to real-world problems
MySQL 도구와 통합 알아보기: MySQL Workbench 같은 DB 관리/모니터링 도구를 익히세요. 또 MySQL과 Python 연동 및 관련 프레임워크도 확인해 실제 개발 환경에서 작업할 수 있음을 보여 주세요.
결론
여기까지입니다! 다음 취업에 도움이 될 MySQL 면접 질문 34선을 정리했습니다. 신입부터 고급 데이터베이스 관리자까지 어떤 포지션이든, MySQL의 기초, 쿼리 최적화, 데이터베이스 관리에 대한 탄탄한 이해가 경쟁력을 높여 줍니다.
다른 데이터베이스 관리 시스템에 대한 지식을 확장하고 싶다면 DataCamp의 SQL 강의를 확인해 보세요.