소프트웨어 개발/데이터·통계

ERD와 SQL 기초: 데이터 모델링부터 JOIN까지

boradora 2026. 7. 28. 12:15

작업일2026. 07

ERD SQL SELECT WHERE JOIN DBeaver

데이터 모델을 설계한 뒤에는 테이블 관계를 ERD로 문서화하고, SQL로 필요한 데이터를 조회합니다.

고객 이름과 주문 상품처럼 여러 테이블에 나뉜 데이터를 함께 조회하려면 어떻게 할까요?
JOIN은 공통 키를 기준으로 여러 테이블의 데이터를 결합합니다.

기본키와 외래키로 테이블을 분리하면 데이터 중복과 불일치를 줄일 수 있습니다. 조회 시에는 필요한 데이터를 결합해야 합니다. 예를 들어 고객 이름은 customers에, 주문 상품은 orders에 있으므로 두 테이블을 연결하는 조건이 필요합니다.

이 글에서는 설계를 표현하는 ERD와 데이터를 조회하는 SQL의 기본 문법을 정리합니다. 문법 자체보다 쿼리가 처리되는 순서를 이해하는 것이 중요합니다.

ERD

ERD(Entity-Relationship Diagram)는 테이블과 테이블 사이의 관계를 시각적으로 표현한 설계 문서입니다. 테이블을 생성하기 전에 데이터 구조를 검토할 수 있으며, 구현 전 변경 비용을 줄이는 데 도움이 됩니다.

ERD는 개발·기획 등 여러 역할이 동일한 데이터 구조를 공유하는 기준이기도 합니다. 개발자는 테이블 구조로, 기획자는 업무 흐름으로 해석할 수 있습니다.

표기 요소

ERD는 개체, 속성, 관계, 카디널리티로 구성됩니다. 각 요소는 데이터 대상, 세부 정보, 연결 방식, 관계의 수를 나타냅니다.

개체 (Entity)
테이블이 될 대상
고객, 주문, 상품
속성 (Attribute)
개체가 갖는 정보. 테이블의 열이 됩니다
고객의 이름, 이메일
관계선 (Relationship)
개체 사이를 잇는 선
고객이 주문을 생성
카디널리티
관계의 개수 표기
1, N

개체는 테이블, 속성은 열로 구현됩니다. ERD를 기준으로 CREATE TABLE 문을 작성할 수 있어 설계와 구현의 대응 관계를 확인하기 쉽습니다.

실제로 그려보면

고객·주문 구조에 상품 테이블을 추가한 예시입니다. 주문 하나에는 여러 상품이 포함될 수 있고, 하나의 상품은 여러 주문에 포함될 수 있으므로 연결 테이블 order_items를 둡니다.

customers
PK customer_id
name
email
joined_at
1  :  N
 
주문한다
orders
PK order_id
FK customer_id
ordered_at
 
1  :  N
 
담는다
order_items
PK item_id
FK order_id
FK product_id
quantity
N  :  1
 
가리킨다
products
PK product_id
name
price
 
점선으로 그린 order_items 가 주문과 상품의 다대다를 1:N 두 개로 풀어주는 연결 테이블입니다. FK 가 붙은 열이 옆 테이블의 PK 를 가리킵니다.

ERD 도구에서는 관계선 끝이 갈라진 형태로 표시되기도 합니다. 이를 까마귀발 표기법(Crow's Foot)이라고 하며, 갈라진 쪽은 여러 개(N), 선 하나로 끝나는 쪽은 하나(1)를 의미합니다.

읽는 순서

ERD를 검토할 때는 다음 항목을 순서대로 확인합니다. 다른 사람이 작성한 ERD를 이해하거나 설계를 점검할 때 동일하게 적용할 수 있습니다.

  1. 이 개체는 무엇을 나타내는가. 테이블 이름과 속성을 보고 어떤 데이터를 담는지 파악합니다
  2. 기본키는 무엇이고 유일성이 보장되는가. 각 행을 구분하는 값이 명확한지 확인합니다
  3. 어떤 외래키가 어떤 테이블을 참조하는가. 관계선을 따라가며 연결을 추적합니다
  4. 카디널리티가 1:1, 1:N, N:M 중 무엇인가. 실제 업무 상황과 맞는 관계인지 검토합니다

카디널리티는 요구사항 변경에 영향을 크게 받습니다. 예를 들어 고객과 주소를 1:1로 모델링하면 여러 배송지를 저장해야 하는 요구가 생겼을 때 구조를 변경해야 합니다. 관계는 현재 데이터가 아니라 업무 규칙을 기준으로 정합니다.

SQL

SQL(Structured Query Language)은 관계형 데이터베이스의 구조와 데이터를 정의·조회·조작하는 표준 언어입니다. PostgreSQL, MySQL, Oracle 등 DBMS마다 세부 문법은 다르지만 SELECT, JOIN 같은 핵심 구문은 공통으로 사용됩니다.

네 가지 분류

SQL 문장은 대상에 따라 네 가지로 분류합니다. 데이터베이스 구조, 데이터, 권한, 트랜잭션 제어가 기준입니다.

DDL
Data Definition Language
테이블 구조를 정의합니다
CREATE ALTER DROP
DML
Data Manipulation Language
데이터를 조회하고 조작합니다
SELECT INSERT UPDATE DELETE
DCL
Data Control Language
권한을 관리합니다
GRANT REVOKE
TCL
Transaction Control Language
작업을 확정하거나 취소합니다
COMMIT ROLLBACK

실습에서는 DML, 특히 SELECT를 가장 자주 사용합니다. 데이터 조회는 애플리케이션과 운영 환경에서 반복적으로 수행되는 작업입니다.

SELECT

SELECT는 조회할 열과 대상 테이블을 지정하는 구문입니다.

SELECT name, email
FROM ecom.customers;

위 쿼리는 customers 테이블에서 nameemail 열을 조회합니다. ecom.스키마 이름입니다. 스키마는 데이터베이스 객체를 논리적으로 구분하고, 이름 충돌과 권한 관리를 지원합니다.

SELECT *는 모든 열을 조회합니다. 운영 코드에서는 필요한 열만 명시하는 편이 좋습니다. 스키마 변경에 따른 결과 변화를 줄이고, 불필요한 데이터 전송을 피할 수 있습니다.

SELECT name
무엇을
FROM customers
어디서
WHERE ...
어떤 행만
ORDER BY ...
어떤 순서로
LIMIT 10
몇 개만

세미콜론(;)은 SQL 문장의 끝을 나타냅니다. 도구 설정에 따라 단일 문장은 생략할 수 있지만, 여러 문장을 실행할 때는 구분자로 필요합니다.

WHERE로 조건 걸기

WHERE는 조건에 맞는 행만 조회하도록 필터링합니다.

SELECT name, email
FROM ecom.customers
WHERE joined_at >= '2025-01-01';

위 쿼리는 2025년 1월 1일 이후에 가입한 고객을 조회합니다. 비교 연산자(=, >, <, >=, <=, <>)와 논리 연산자(AND, OR, NOT)를 조합할 수 있습니다.

문자열과 날짜 리터럴은 홑따옴표로 감쌉니다. PostgreSQL에서 겹따옴표는 식별자에 사용하므로 값에 사용하면 오류가 발생할 수 있습니다.

조건 쓰는 법
범위 BETWEEN 10 AND 20 10 이상 20 이하 (양 끝 포함)
목록 IN ('서울', '부산') 둘 중 하나와 같음
부분 일치 LIKE '김%' 김으로 시작 (%는 아무 글자 여러 개)
빈 값 IS NULL 값이 없음
= NULL은 NULL 값을 비교하는 방식으로 사용할 수 없습니다. NULL과의 비교 결과는 참이나 거짓이 아닌 UNKNOWN이므로 조회 결과가 0건이 될 수 있습니다. NULL 값을 찾을 때는 IS NULL, NULL이 아닌 값을 찾을 때는 IS NOT NULL을 사용합니다.

해석되는 순서

SQL의 작성 순서와 논리적 처리 순서는 다릅니다. 처리 순서를 이해하면 별칭과 집계 관련 오류를 분석하는 데 도움이 됩니다.

FROM
 
WHERE
 
GROUP BY
 
SELECT
 
ORDER BY
 
LIMIT
SELECT 가 네 번째로 처리되는 것이 중요합니다. SELECT 에서 붙인 별칭은 그때까지 존재하지 않으므로 WHERE 에서 쓸 수 없고, ORDER BY 에서는 쓸 수 있습니다.

JOIN

JOIN은 공통 열을 기준으로 두 개 이상의 테이블을 하나의 결과 집합으로 결합하는 구문입니다. 정규화로 분리한 데이터는 조회 목적에 따라 JOIN으로 결합합니다.

SELECT c.name, o.item
FROM ecom.customers c
JOIN ecom.orders o
  ON c.customer_id = o.customer_id;

customers 뒤의 corders 뒤의 o별칭입니다. 두 테이블에 customer_id처럼 같은 이름의 열이 있으면 c., o.로 소속 테이블을 명시해야 합니다.

ON에는 테이블을 연결할 조건을 지정합니다. 위 쿼리는 같은 customer_id를 가진 행을 결합하므로, 고객 정보는 해당 고객의 주문 건수만큼 결과에 나타납니다.

customers
customer_id
name
1
보라
2
도라
orders
customer_id
item
1
키보드
1
마우스
ON c.customer_id = o.customer_id
JOIN 결과
name
item
보라
키보드
보라
마우스
보라의 이름은 주문이 두 건이므로 두 행에 나타납니다. 도라는 주문이 없으므로 INNER JOIN 결과에 포함되지 않습니다.

JOIN의 종류

주문이 없는 고객까지 조회해야 한다면 다른 JOIN 유형을 사용해야 합니다. JOIN 유형은 어느 테이블의 행을 결과에 유지할지에 따라 구분됩니다.

종류 남는 행 쓰는 상황
INNER JOIN 양쪽에 짝이 있는 행만 기본값. JOIN만 적으면 이것입니다
LEFT JOIN 왼쪽 테이블은 전부, 오른쪽은 짝이 있을 때만 주문이 없는 고객도 목록에 넣을 때
RIGHT JOIN 오른쪽 테이블은 전부 LEFT로 순서를 바꿔 쓰는 경우가 많습니다
FULL OUTER JOIN 양쪽 전부 어느 쪽에만 있는 데이터를 찾아낼 때
-- 주문이 없는 고객도 함께 조회
SELECT c.name, o.item
FROM ecom.customers c
LEFT JOIN ecom.orders o
  ON c.customer_id = o.customer_id;

LEFT JOIN을 사용하면 도라도 결과에 포함되고, 주문이 없는 경우 itemNULL입니다. 후속 집계나 계산에서는 NULL 값을 고려해야 합니다.

실습 환경

실습에서는 PostgreSQL을 데이터베이스로, DBeaver를 클라이언트 도구로 사용합니다. PostgreSQL은 데이터를 저장하고 SQL을 처리하는 서버이며, DBeaver는 서버에 접속해 SQL을 실행하고 결과를 표시하는 클라이언트입니다.

  • PostgreSQL: 오픈소스 관계형 DBMS입니다. 표준 SQL 지원이 넓어 학습과 실무 환경에서 많이 사용됩니다
  • DBeaver: 여러 DBMS에 연결할 수 있는 데이터베이스 클라이언트입니다. ERD 생성 기능으로 테이블 관계를 확인할 수 있습니다
오류 메시지에는 원인 분석에 필요한 정보가 포함됩니다. SQL 오류는 대개 오류 위치와 토큰을 알려줍니다. column "x" does not exist는 열 이름을, relation "y" does not exist는 테이블 이름과 스키마 접두어를 확인해야 한다는 의미입니다.

정리하면

  • ERD: 개체·속성·관계선·카디널리티 네 가지로 그립니다. 개체가 테이블, 속성이 열이 되니 그림과 코드가 그대로 대응합니다
  • SQL 분류: 구조는 DDL, 데이터는 DML, 권한은 DCL, 확정과 취소는 TCL
  • SELECT: 적는 순서와 처리 순서가 다릅니다. FROMWHERESELECTORDER BY
  • JOIN: 짝이 있는 행만 볼 때는 INNER, 한쪽을 전부 남길 때는 LEFT
  • NULL: = NULL은 조용히 0건을 돌려줍니다. IS NULL을 씁니다
한 줄 정리: ERD로 데이터 구조를 설계하고, DDL로 테이블을 생성합니다. SELECTWHERE로 데이터를 조회하고, JOIN으로 여러 테이블의 데이터를 결합합니다.

참고

다음 글에서는 서비스 규모에서 관계형 데이터베이스의 한계를 검토하고, 벡터 DB 같은 대안을 선택하는 기준을 정리합니다.