
⚡ 한 줄 요약: SQL의 정밀한 CRUD 연산과 조인(JOIN), 페이징 기법을 통해 대규모 데이터를 효율적으로 제어하고, 서비스 성격에 맞는 NoSQL 도입 기준을 확립합니다.
지난 포스팅에서 데이터베이스의 설계를 다뤘다면, 이제는 그 설계된 데이터에 생명력을 불어넣을 차례입니다.
🧐 Why:
SELECT 같은 기초 문법은 쉽지만, 이를 대규모 트래픽 환경에서 안전하고 성능 지향적으로 사용하는 법을 알아야 하기 때문입니다.🎯 Goal:
SQL(Structured Query Language)은 관계형 데이터베이스에서 데이터를 조작하고 관리하기 위해 사용하는 표준 언어입니다.
개발자가 DB와 대화하기 위한 '공통 언어'라고 이해하면 됩니다.
가장 많이 사용하는 조회(SELECT) 기능의 핵심 포인트는 다음과 같습니다:
전체 컬럼 조회:
* (Asterisk) 키워드를 사용합니다.SELECT * FROM users;특정 컬럼 필터링:
SELECT name, email FROM users;조건 및 정렬:
WHERE 절로 특정 조건의 데이터만 필터링하거나, ORDER BY를 통해 오름차순(ASC) 또는 내림차순(DESC)으로 정렬할 수 있습니다.데이터베이스의 상태를 변화시키는 작업들로, 실무에서는 CRUD라고 부르는 연산의 핵심입니다.
INSERT (Create): 새로운 데이터를 추가합니다.
INSERT INTO users (name, email) VALUES ('가나다', 'abcd@example.com');UPDATE (Update): 기존 데이터를 수정합니다.
WHERE 절과 함께 사용하여 엉뚱한 데이터가 수정되지 않도록 주의해야 합니다.DELETE (Delete): 데이터를 삭제합니다.
DELETE FROM users;ORDER BY와 LIMIT 키워드를 조합하면 특정 기준에 부합하는 데이터 일부만 삭제할 수 있습니다.DELETE FROM orders ORDER BY order_date ASC LIMIT 1;GROUP BY의 제약
WHERE나 ORDER BY는 수정/삭제/조회 등 다양한 상황에서 쓰이지만, 데이터를 그룹화하는 GROUP BY는 오직 조회(SELECT) 상황에서만 사용됩니다.WHERE절의 중요성
UPDATE나 DELETE 실행 시 WHERE 절을 빠뜨리면 테이블 전체 데이터가 변경되거나 삭제되는 대참사가 일어날 수 있으니 항상 확인이 필요합니다.ORDER BY나 LIMIT을 활용하면 정밀한 데이터 타겟팅이 가능해집니다.집계 함수는 말 그대로 여러 행의 데이터를 모아 하나의 결과(통계치)를 만들어내는 함수입니다.
데이터를 집계하기 위해 사용되므로 주로 SELECT 키워드와 함께 사용됩니다.
COUNT() - 데이터 개수 구하기
NULL 값은 자동으로 제외됩니다.SUM() - 합계 구하기
NULL 값은 제외됩니다.AVG() - 평균 구하기
NULL 값은 계산에서 빠집니다.MAX() / MIN() - 최댓값/최솟값 구하기
집계 함수를 단독으로 쓰면 결과가 숫자 하나로 딱 떨어지지만,
뒤에 나올 GROUP BY와 섞어 쓰면 멋진 테이블 형태로 결과를 얻을 수 있습니다.
💡 비유로 이해하기
전교생의 성적표가 쌓여 있을 때, "전체 평균은?"이라고 물으면 숫자 하나가 나옵니다. (단독 사용)
하지만 "반별로 평균은?"이라고 물으면 각 반과 그에 맞는 숫자가 나열된 표가 필요하게 됩니다. (GROUP BY사용)
NULL의 마법
NULL을 무시합니다.AVG() 계산 시 NULL 행 자체를 모수(나누는 수)에서 제외할지, 아니면 0으로 치환해서 계산할지에 따라 결괏값이 달라질 수 있다는 점을 유의해야 합니다.GROUP BY의 위치
GROUP BY와 함께 쓸 때 비로소 강력해집니다.SELECT하면 에러가 발생하거나 의도치 않은 행이 조회될 수 있습니다.SELECT와 함께 쓰이며, GROUP BY 사용 시 그룹 기준 컬럼은 반드시 SELECT 절에 명시해야 합니다.서브쿼리는 SQL문 내부에 포함된 또 다른 SQL문을 의미합니다.
메인 쿼리에서 필요한 값을 동적으로 가져와야 할 때 주로 사용하며, 실행 결과에 따라 메인 쿼리의 조건이나 데이터가 결정됩니다.
서브쿼리는 위치에 따라 크게 세 가지 용도로 나뉩니다:
WHERE 절에서 사용: 특정 값을 동적으로 조건 설정할 때 씁니다.
SELECT MAX(salary)로 최고 급여액을 구한 뒤 이를 조건으로 사용합니다.FROM 절에서 사용: 서브쿼리 자체를 하나의 임시 테이블처럼 사용합니다.
SELECT 절에서 사용: 특정 컬럼 값을 동적으로 계산하여 가져올 때 사용합니다.
SELECT NAME
, SALARY
, (
SELECT AVG(SALARY)
FROM EMPLOYEES
WHERE DEPARTMENT = E.DEPARTMENT
) AS DEPT_AVG_SALARY
FROM EMPLOYEES E;조인은 두 개 이상의 테이블을 결합하여 데이터를 조회하는 방법입니다.
실제 실무에서는 데이터 중복을 최소화하고 효율적으로 관리하기 위해 정보를 여러 테이블에 나누어 저장하는데, 이를 다시 연결해서 보고 싶을 때 조인을 사용합니다.
| 조인 종류 | 설명 | 데이터 포함 범위 |
|---|---|---|
| INNER JOIN | 두 테이블에서 공통된 데이터만 반환합니다. | 교집합 |
| LEFT JOIN | 왼쪽 테이블의 모든 데이터와 오른쪽의 일치하는 데이터를 반환합니다. | 왼쪽 기준(없으면 NULL) |
| RIGHT JOIN | 오른쪽 테이블의 모든 데이터와 왼쪽의 일치하는 데이터를 반환합니다. | 오른쪽 기준(없으면 NULL) |
| FULL JOIN | 두 테이블의 모든 데이터를 포함합니다. | 합집합 |
1) INNER JOIN (교집합)
가장 기본이 되는 조인으로, 양쪽 테이블에 모두 값이 존재하는 데이터만 가져옵니다.
2) LEFT JOIN (왼쪽 기준)
왼쪽 테이블(주로 기준이 되는 테이블)의 데이터는 무조건 다 보여주고, 오른쪽 테이블에 매칭되는 값이 없으면 NULL로 표시합니다.


LEFT OUTER JOIN 이라고도 부릅니다.NULL로 표시됩니다.3) RIGHT JOIN (오른쪽 기준)
LEFT JOIN과 반대로 오른쪽 테이블을 기준으로 데이터를 가져옵니다.
LEFT JOIN으로 쓰는 경우가 더많습니다.OUTER 키워드
LEFT JOIN은 사실 LEFT OUTER JOIN의 줄임말입니다.OUTER를 생략해도 결과는 같습니다.NULL의 의미
LEFT JOIN 결과에서 NULL이 보인다면, 그것은 "데이터가 잘못된 것"이 아니라 "기준 테이블에는 데이터가 있지만 연결된 테이블에는 해당 값이 없음"을 의미하는 정상적인 결과입니다.페이징이란 대량의 데이터를 한꺼번에 불러오는 것이 아니라, 사용자가 보기 편하도록 일정한 단위(페이지)로 나누어 조회하는 기법을 말합니다.
우리가 인터넷 쇼핑을 할 때 수만 개의 상품이 한 페이지에 다 나오는 것이 아니라 1페이지, 2페이지로 나누어 보이는 것이나, 블로그의 게시글 목록이 정해진 개수만큼 노출되는 것이 모두 이 페이징 기술을 사용한 사례입니다.
핵심 목적:
구현 키워드:
LIMIT과 OFFSET을 조합하여 특정 범위의 데이터를 가져옵니다.OFFSET: 조회를 시작할 위치를 지정합니다. (0부터 시작)LIMIT: 조회할 데이터의 개수를 지정합니다.페이징 쿼리 예시
1페이지 (1 ~ 10번째)
SELECT * FROM users LIMIT 10 OFFSET 0;
3페이지 (21 ~ 30번째)
SELECT * FROM users LIMIT 10 OFFSET 20;
단순히 OFFSET을 쓰면 다 해결될 것 같지만, 실무에서는 아주 큰 함정이 하나 있습니다.
바로 데이터베이스가 데이터를 읽는 방식 때문입니다.
데이터베이스는 OFFSET으로 지정한 위치까지의 데이터를 내부적으로 모두 읽고 나서 그 다음 LIMIT만큼을 반환합니다.
예를 들어 1,000번째 데이터를 보려면 DB는 내부적으로 0번째부터 999번째 데이터까지 쭉 스캔하는 과정을 거쳐야만 합니다.
결국 데이터 양이 많아질수록 OFFSET 값이 커지면 조회 속도는 눈에 띄게 느려질 수 밖에 없습니다.
💻 참고
실무에서는 이런 성능 저하를 막기 위해
OFFSET대신 인덱스 기반 방식(No-offset)을 더 선호합니다.마지막으로 읽은 데이터의 ID 값을 기준으로 다음 데이터를 조회하는 방식인데, 특히 프론트엔드에서 '무한 스크롤'을 구현할 때 백엔드와 이 방식을 논의하며 훨씬 전문적인 인상을 줄 수 있습니다.
사용자 편의 vs 시스템 성능
LIMIT과 OFFSET의 순서
LIMIT을 먼저 쓰고 OFFSET을 나중에 씁니다.OFFSET보다 인덱스 기반 조회를 권장합니다.NoSQL은 'Not Only SQL'의 약자로, 정해진 테이블 구조(스키마) 없이 데이터를 자유로운 형태로 저장하고 관리하는 방식입니다.
과거에는 데이터의 정합성이 최우선이었지만, 이제는 빅데이터 처리, 높은 확장성, 빠른 데이터 접근이 더 중요한 시대가 되었죠.
특히 다음과 같은 대규모 트래픽 환경에서 NoSQL은 진가를 발휘합니다:
실시간 채팅 및 SNS:
로그 저장:
캐시 서버:
중요한 점은 NoSQL이 RDBMS를 대체하는 것이 아니라는 겁니다.
RDBMS의 강력한 정합성 장점은 여전히 유효하기 때문에,
실무에서는 두 시스템의 장점을 결합한 혼합 구조를 선택하는 경우가 더 많습니다.
💻 참고
흔히 접하는 Firebase나 캐싱을 위한 Redis가 대표적인 NoSQL입니다.
실무에서는 정형화된 유저 정보는 MySQL에, 실시간 알림이나 복잡한 게시글 메타데이터는 NoSQL에 나누어 담는 전략을 자주 사용하니 이 '조합'의 이유를 아는 것이 중요합니다.
NoSQL은 데이터의 형태에 따라 여러 가지 모델을 지원합니다.
Key-Value 모델
Document 모델
Column 모델
💡 비유로 이해하기
RDBMS가 칸이 딱딱 나누어진 '서류 보관함'이라면, NoSQL은 내용물을 자유롭게 담을 수 있는 '커다란 바구니'와 같습니다.
보관함은 찾기엔 정확하지만 담을 때 형식을 맞춰야 하고, 바구니는 아무거나 빠르게 던져 놓기 좋습니다.
NoSQL은 SQL을 안 쓴다?
NoSQL이 무조건 빠르다?
안전한 데이터 제어
SELECT *를 활용하되, UPDATE나 DELETE 시에는 WHERE 만으로 안심하지 마세요.데이터 결합의 미학
JOIN을, 동적인 필터링이나 개선이 필요할 때는 서브쿼리를 선택하세요.성능 최적화의 핵심, 페이징
OFFSET 방식은 데이터가 많아질수록 읽기 속도가 기하급수적으로 느려집니다.NoSQL