A-1: 데이터베이스 개념과 첫 SELECT — 표·행·열, 그리고 의미 있는 첫 조회
목차 27
안녕하세요, 데이터베이스의 세계로 함께 가는 홍순구 튜터입니다. 다시 만나서 반가워요.
지난 시간엔 우리 컴퓨터 안에 Oracle 데이터베이스를 띄우고, SELECT * FROM member 한 줄로 회원 8명을 조회하는 데 성공했죠. 그때는 "환경이 살아있다"를 확인하는 게 목표라, 결과가 화면에 뜨는 것만 즐겨도 충분했습니다.
오늘은 한 발 더 들어갑니다. 그 한 줄이 정확히 무슨 뜻이었는지, 화면에 떴던 그 표는 데이터베이스 안에서 어떻게 생긴 것인지를 제대로 알아볼 거예요. 이름 없이 슬쩍 지나갔던 테이블·행·열을 오늘 정식으로 만나고, 회원을 한 명 한 명 구분해주는 기본키라는 개념도 처음 마주합니다.
그리고 SELECT * 로 "그냥 다 보여줘"만 했던 지난 시간과 달리, 오늘은 "이 열만 골라서 보여줘" 같은 의미 있는 조회를 직접 작성해봅니다.
오늘의 여정 — 표 한 장을 제대로 읽는 법
① 관계형 모델 — 데이터를 '표' 모양으로 담는다
② 테이블 · 행(row) · 열(column) — 표를 이루는 세 이름
③ 기본키(primary key) — 행 하나를 콕 집어 구분하는 열
④ 'SELECT *' 해부 — 그 첫 줄이 정확히 무슨 뜻이었나
⑤ 원하는 열만 골라 조회 — 모든 열 말고 필요한 열만
⑥ 두 표의 연결 — 회원과 게시물, 관계형의 '관계' 맛보기
│
▼
"이 조회는 정확히 8건" 을 눈으로 검산하기까지
지난 시간 띄워둔 환경 그대로 이어서 갑니다. 혹시 컨테이너를 껐다면 docker compose up -d 로 다시 켜고 시작할게요.
💡 오늘 수업의 핵심 — "데이터는 표(테이블) 모양으로 담기고, SELECT 로 원하는 만큼 골라 꺼낸다"
🎯 학습 목표
- 관계형 데이터베이스가 데이터를 표(테이블) 형태로 담는다는 것을 이해하고, 테이블·행(row)·열(column)을 구분할 수 있다.
- 각 행을 유일하게 구분하는 기본키(primary key) 가 왜 필요한지 설명할 수 있다.
SELECT *와SELECT 컬럼명의 차이를 알고, 원하는 열만 골라 조회하는 SQL 을 직접 작성할 수 있다.
Step 1: "데이터베이스, 데이터를 어떤 모양으로 담을까?"
지난 시간에 우리는 데이터베이스를 "데이터를 담아두는 거대한 창고", 그 창고를 관리하는 프로그램을 DBMS 라고 불렀어요. 그리고 우리가 쓰는 DBMS 가 Oracle 이고요.
오늘 던질 질문은 이겁니다. 그 창고는 데이터를 어떤 모양으로 담을까요?
여러분이 친구 명단을 정리한다고 생각해 보세요. 아마 엑셀 같은 표를 떠올릴 겁니다. 맨 윗줄에 이름·전화번호·생일을 적고, 그 아래로 한 명씩 가로로 채워 넣죠. Oracle 도 똑같아요. 데이터를 이런 표 모양으로 차곡차곡 담습니다.
이렇게 데이터를 표 형태로 담는 방식을 관계형 모델(relational model) 이라고 하고, 이 방식을 따르는 DBMS 를 관계형 데이터베이스(RDBMS, Relational DBMS) 라고 불러요. Oracle 도, 많이 들어보셨을 MySQL 도 모두 관계형 데이터베이스입니다. 세상에서 가장 널리 쓰이는 방식이죠. ★빈출 — 이 "관계형 데이터베이스와 SQL 개요" 가 SQLD 시험 2과목의 출발점이기도 해요.
왜 하필 표일까요? 표는 누구나 한눈에 읽습니다. 가로줄과 세로 칸으로 가지런히 정렬되어 있어서, "누가 어떤 값을 가지고 있는지" 가 명확하거든요. 데이터를 어지럽게 늘어놓는 대신 격자에 맞춰 담는 것, 그게 관계형 데이터베이스의 출발점입니다.
💡 한 줄 정리
관계형 데이터베이스(RDBMS)는 데이터를 표(테이블) 모양으로 담는다. Oracle 과 MySQL 이 그 대표다.
🙋 학생 질문 — "튜터님, '관계형'이라는 이름은 왜 붙었어요? 그냥 표인데 왜 '관계'예요?"
이름이 조금 헷갈리죠. 두 가지 유래가 섞여 있어요.
원래 '관계(relation)'는 수학에서 표 그 자체를 가리키는 말이었어요. 그래서 표로 데이터를 다루는 방식에 '관계형'이라는 이름이 붙었습니다. 또 하나, 표들이 서로 연결되기도 해요. 회원 표와 게시물 표가 "누가 썼나"로 이어지는 식으로요. 이 연결도 '관계'라고 부릅니다.
오늘 Step 6 에서 그 표끼리의 연결을 살짝 맛볼 거예요. 더 깊은 이야기는 다음 시간 데이터 모델링에서 제대로 다룹니다. 지금은 "표로 담고, 표끼리 이어진다" 정도만 기억해 주세요.
Step 2: "표 하나를 뜯어보자 — 테이블·행·열"
말로만 표라고 하면 감이 안 오죠. 지난 시간에 봤던 그 회원 조회를 다시 띄워봅시다.
SELECT * FROM member;
실행하면 회원 8명이 이렇게 돌아옵니다.
| member_id | username | nickname | bio |
|---|---|---|---|
| 1 | jaehoon | 김재훈 | 백엔드 공부 중 |
| 2 | minji | 이민지 | 사진과 커피 |
| 3 | seungwoo | 박승우 | (빈칸) |
| 4 | haru | 정하루 | 일상 기록 |
| 5 | doyeon | 최도연 | (빈칸) |
| 6 | jisoo | 강지수 | 여행 좋아해요 |
| 7 | taeyang | 윤태양 | 운동하는 개발자 |
| 8 | narae | 한나래 | (빈칸) |
이 표를 뜯어보면 세 가지 이름이 나옵니다. 지난 시간에 슬쩍 봤던 그 이름들을 이제 정식으로 만나요.
- 테이블(table) — 표 전체를 가리킵니다. 우리가 본 건
member(회원) 테이블이에요. - 행(row) — 가로로 놓인 한 줄. 회원 한 명이 한 행을 차지합니다. 8명이니까 8행이죠. '레코드(record)'라고도 불러요.
- 열(column) — 세로로 놓인 한 칸.
username·nickname처럼 어떤 항목인지를 나타냅니다. '속성(attribute)'이라고도 합니다.
엑셀에 그대로 대응돼요. 시트 하나가 테이블, 가로줄이 행, 세로 칸이 열입니다.
세로 한 칸이 '열(column)' 가로 한 줄이 '행(row)'
┌───────────┬──────────┬──────────┐
│ member_id │ username │ nickname │ ← 맨 윗줄 = 열 이름표
├───────────┼──────────┼──────────┤
│ 1 │ jaehoon │ ... │ ← 한 줄(행) = 회원 한 명
│ 2 │ minji │ ... │
└───────────┴──────────┴──────────┘
↑
열 하나 = 모든 회원의 같은 항목 (예: username 칸 전체)
자기소개(bio)를 안 쓴 회원은 (빈칸)으로 남아 있죠? 이 빈칸을 데이터베이스에서 뭐라고 부르고 어떻게 다루는지는 SQL 을 더 깊이 배우면서 차차 만납니다. 오늘은 "비어 있을 수도 있다" 정도만 봐두세요.
💡 한 줄 정리
표 전체가 테이블, 가로줄 하나가 행(row), 세로 칸 하나가 열(column)이다. 회원 8명 = 8행, 항목 4개 = 4열.
🙋 학생 질문 — "튜터님, 행이랑 열이 자꾸 헷갈려요. 쉽게 구분하는 법 없을까요?"
처음엔 다들 헷갈려요. 이렇게 외워보세요.
행(row)은 가로예요. 영어 'row'가 '노를 젓다'의 그 줄, 즉 가로로 죽 늘어선 줄이라고 떠올리면 됩니다. 우리 표에선 회원 한 명이 가로 한 줄을 차지하죠. 열(column)은 세로예요. 'column'은 건물의 기둥인데, 기둥은 세로로 서 있잖아요. 표에선 username 같은 항목이 위에서 아래로 세로로 이어집니다.
"행은 가로 한 명, 열은 세로 한 항목" — 이 한 문장만 기억하면 다시는 안 헷갈려요.
Step 3: "한 명 한 명 어떻게 구분하지? — 기본키"
표를 보다가 이런 질문이 떠오를 수 있어요. 수많은 회원 중에서 특정 한 명을 콕 집어내려면 무엇으로 가리켜야 할까요?
이름(nickname)으로 가리키면 될까요? 위험합니다. 인스타그램 회원이 수백만 명이라면 '김민지'라는 이름을 가진 사람이 여럿일 수 있어요. "김민지를 찾아줘" 하면 데이터베이스가 누구를 줘야 할지 알 수 없죠.
그래서 표마다, 각 행을 절대 겹치지 않게 구분해주는 열 을 하나 둡니다. 이걸 기본키(primary key), 줄여서 PK 라고 불러요. member 테이블에서는 member_id 가 그 역할을 합니다.
SELECT member_id, username FROM member;
| member_id | username |
|---|---|
| 1 | jaehoon |
| 2 | minji |
| 3 | seungwoo |
| ... | ... |
| 8 | narae |
member_id 는 1, 2, 3 … 8 처럼 회원마다 다른 값을 가지고, 절대 겹치지 않으며, 비어 있는 법도 없습니다. 그래서 "member_id 가 3인 회원"이라고 하면 단 한 명, 박승우만 정확히 가리켜져요.
비유하자면 주민등록번호나 학번 같은 거예요. 같은 반에 '김민준'이 둘 있어도 학번은 절대 안 겹치니까, 학번만 대면 누구인지 단번에 정해지죠. 기본키가 바로 그 학번 역할입니다.
기본키는 두 가지 성질을 맛보기로 기억해두면 좋아요. 겹치지 않는다(유일하다), 그리고 반드시 값이 있다(비어 있을 수 없다). 이 성질을 더 정밀하게 따지는 건 다음 시간 데이터 모델링에서 본격적으로 다룹니다.
💡 한 줄 정리
기본키(primary key, PK)는 각 행을 유일하게 구분하는 열이다. member 표에서는 member_id. 주민번호·학번처럼 겹치지 않고 비어 있지 않다.
🙋 학생 질문 — "튜터님, username 도 안 겹치잖아요. 그걸 기본키로 쓰면 안 돼요?"
날카로운 질문이에요. 사실 username(영문 아이디)도 우리 표에선 겹치지 않게 관리됩니다. 그래서 기본키로 쓸 수도 있어요.
그런데 보통은 member_id 같은 단순한 숫자 를 기본키로 선호합니다. 이유가 있어요. 아이디는 사용자가 나중에 바꾸고 싶어할 수 있잖아요? 만약 아이디를 기본키로 썼는데 누군가 아이디를 바꾼다면, 그 회원을 가리키던 다른 데이터(예: 게시물)가 전부 어긋나버립니다. 반면 한 번 정한 숫자 member_id 는 평생 안 바꾸기로 약속해두면 그런 사고가 없죠.
"기본키는 변하지 않는 값으로" — 이 감각만 잡아두세요. 어떤 열을 기본키로 고를지 그 기준은 다음 시간에 깊게 파봅니다.
Step 4: "그래서 첫 SELECT 가 무슨 뜻이었나 — SELECT * 해부"
이제 지난 시간 그 한 줄로 돌아가봅시다. 우리가 무심코 실행했던 이 문장이요.
SELECT * FROM member;
한 토막씩 끊어 읽으면 뜻이 또렷해집니다.
SELECT— "조회해라", 즉 무엇을 보여줄지 고르는 명령이에요.- 별표(
*) — "모든 열". 별표 하나가 "이 표의 열 전부"를 뜻합니다. FROM member— "member 테이블에서".
이어 붙이면 "member 테이블에서 모든 열을 보여줘" 가 됩니다. 그래서 결과로 회원 8명(8행)이, member_id·username·nickname·bio 네 개 열을 모두 달고 돌아온 거예요.
SQL 의 매력이 여기 있어요. 거의 영어 문장처럼 읽힙니다. SELECT(선택해라) … FROM(~로부터). 질문(SQL)을 던지면 데이터베이스가 답(결과)을 돌려주는, 지난 시간에 봤던 그 흐름 그대로입니다.
💡 한 줄 정리
SELECT * FROM member 는 "member 테이블에서 모든 열을 보여줘"라는 뜻이다. 별표(*)가 '모든 열'을 가리킨다.
🙋 학생 질문 — "튜터님, SELECT 랑 FROM 은 꼭 대문자로 써야 하나요?"
문법상으로는 대소문자를 가리지 않아요. select * from member 라고 소문자로 써도 똑같이 동작합니다.
다만 관례가 있어요. SELECT·FROM 같은 명령어는 대문자, member·username 같은 테이블·열 이름은 소문자로 씁니다. 이렇게 구분해두면 "어디까지가 SQL 명령이고 어디부터가 내 데이터 이름인지" 가 한눈에 보여요. 우리 강의도 이 관례를 따라갑니다. 눈에 익혀두면 남이 짠 SQL 도 훨씬 빨리 읽힙니다.
Step 5: "다 말고 원하는 열만 — 컬럼 골라 조회"
별표(*)는 모든 열을 가져옵니다. 그런데 늘 전부가 필요한 건 아니에요. 회원 목록에서 영문 아이디와 한글 이름만 보고 싶을 때가 있죠.
그럴 땐 별표 대신 원하는 열 이름을 콤마로 나열 하면 됩니다.
SELECT username, nickname FROM member;
| username | nickname |
|---|---|
| jaehoon | 김재훈 |
| minji | 이민지 |
| seungwoo | 박승우 |
| haru | 정하루 |
| doyeon | 최도연 |
| jisoo | 강지수 |
| taeyang | 윤태양 |
| narae | 한나래 |
결과를 보면 username 과 nickname 두 열만 나왔어요. member_id 와 bio 는 빠졌죠. 마치 명함첩 전체에서 "이름과 전화번호만" 뽑아 보는 것과 같아요. 필요한 정보만 골라 보는 겁니다.
여기서 중요한 포인트 하나. 행 수는 여전히 8 입니다. 회원을 거른 게 아니라 열만 골랐으니까요. 열을 고르는 것과 회원(행)을 고르는 것은 전혀 다른 일이에요. 이 차이를 지금 확실히 잡아두면 나중에 헷갈리지 않습니다.
열 순서도 마음대로 바꿀 수 있어요. SELECT nickname, username FROM member 라고 쓰면 한글 이름이 먼저 나옵니다.
⚠️ 함정 — 배울 때는
SELECT *가 편하지만, 실무에서는 필요한 열만 콕 집어 쓰는 걸 권장해요. 늘 모든 열을 가져오면 안 쓸 데이터까지 함께 옮겨오느라 느려질 수 있거든요. "필요한 열만 고르는" 습관을 지금부터 들여두면 좋습니다.
이게 바로 지난 시간에 예고했던 "원하는 열만 골라 보기"의 정체예요. 그럼 "원하는 회원만"(특정 조건에 맞는 행만) 골라 보는 건 어떻게 할까요? 그건 조건을 거는 법을 따로 배워야 하는데, 그 본격적인 조회는 SQL 을 직접 짜기 시작하는 다음 카테고리에서 깊게 다룹니다.
💡 한 줄 정리
SELECT username, nickname FROM member 는 원하는 두 열만 골라 조회한다. 열을 고르는 것은 행을 거르는 것과 다르다 — 행 수는 그대로 8.
🙋 학생 질문 — "튜터님, 열 이름을 잘못 쓰면 어떻게 돼요? 없는 이름을 적으면요?"
에러가 납니다. 예를 들어 username 을 usernmae 처럼 오타로 쓰면, 데이터베이스가 "그런 열은 없다(invalid identifier)"며 조회를 거부해요.
겁먹을 필요는 없어요. 오타는 누구나 흔히 하는 실수고, 데이터베이스가 "어디가 문제인지" 친절히 알려주거든요. 에러 메시지가 떴다고 컴퓨터가 망가지는 게 아니에요. 메시지를 읽고 철자만 고쳐 다시 실행하면 됩니다. 앞으로 에러는 적이 아니라 "여기 좀 봐달라"는 안내라고 생각해 주세요.
Step 6: "회원 말고 게시물도 — 두 번째 테이블과 '관계' 맛보기"
우리 데이터베이스에는 회원만 있는 게 아니에요. 회원들이 올린 게시물 도 있습니다. 표가 하나 더 있다는 뜻이죠.
SELECT * FROM post;
| post_id | member_id | caption | created_at |
|---|---|---|---|
| 1 | 1 | 첫 게시물입니다 #안녕 | 2026-06-01 |
| 2 | 1 | 오늘도 코딩 #개발 | 2026-06-02 |
| 3 | 1 | 주말 산책 | 2026-06-07 |
| 4 | 2 | 아침 커피 한 잔 #카페 | 2026-06-03 |
| 5 | 2 | 필름 카메라 #사진 | 2026-06-05 |
| 6 | 2 | 노을 #하늘 | 2026-06-08 |
| 7 | 3 | 맛집 탐방 | 2026-06-04 |
| 8 | 3 | 오늘의 점심 | 2026-06-06 |
| 9 | 4 | 소소한 일상 #데일리 | 2026-06-05 |
| 10 | 4 | 집밥 기록 | 2026-06-09 |
| 11 | 5 | 운동 끝 #헬스 | 2026-06-06 |
| 12 | 6 | 제주도 여행 #여행 | 2026-06-10 |
게시물 12개가 나왔어요. post 테이블의 열은 post_id(게시물 기본키)·member_id·caption(글 내용)·created_at(작성일)입니다.
여기서 흥미로운 점 하나. post 표에 member_id 열이 있죠? 이건 "이 게시물을 누가 썼나"를 가리킵니다. 1번 게시물의 member_id 가 1이니까, 1번 회원(jaehoon)이 쓴 글이에요. 2·3번 게시물도 member_id 가 1이니 역시 jaehoon 의 글이고요.
post 표 (일부)
post_id member_id caption
─────── ───────── ──────────────────
1 1 첫 게시물입니다 #안녕
2 1 오늘도 코딩 #개발
4 2 아침 커피 한 잔 #카페
│
└──▶ 이 숫자가 member 표의 member_id 를 가리킨다
(1 = jaehoon, 2 = minji …)
이게 바로 관계형 데이터베이스의 '관계'예요. 회원 정보를 게시물마다 다시 적는 대신, 회원은 member 표에·게시물은 post 표에 따로 담아두고, member_id 라는 열로 둘을 이어주는 거죠. 이렇게 다른 표를 가리키는 열을 외래키(foreign key) 라고 부르는데, 정식 설계 이야기는 다음 시간으로 미뤄둘게요.
데이터를 살펴보면 재미있는 구석이 있어요. post 표의 member_id 에 7번과 8번은 한 번도 안 나옵니다. 즉 7번(taeyang)과 8번(narae)은 게시물이 한 개도 없는 회원이에요. 나중에 회원과 게시물을 연결해서 보는 '조인'을 배울 때, 이렇게 게시물이 0개인 회원들이 아주 중요한 연습 상대가 됩니다. 지금은 "이런 경계 사례가 일부러 들어있구나" 정도만 봐두세요.
💡 한 줄 정리
게시물은 post 테이블에 따로 담기고, post.member_id 가 "누가 썼는지"를 member 표와 연결한다. 이 표끼리의 연결이 관계형의 '관계'다.
🙋 학생 질문 — "튜터님, 회원이랑 게시물을 왜 한 표에 안 넣어요? 같이 적으면 편하잖아요?"
충분히 그럴 수 있는 생각이에요. 그런데 한 회원이 게시물을 여러 개 올리잖아요? jaehoon 만 해도 게시물이 3개죠.
만약 한 표에 다 적는다면, jaehoon 의 이름·자기소개 같은 회원 정보가 게시물마다 똑같이 세 번 반복돼서 들어갑니다. 회원이 정보를 바꾸기라도 하면 세 군데를 다 고쳐야 하고, 한 군데라도 빠뜨리면 데이터가 어긋나죠.
그래서 회원은 회원 표에 한 번만, 게시물은 게시물 표에 두고, member_id 로 잇는 겁니다. 이렇게 나눠 담는 게 왜 좋은지(이걸 '정규화'라고 해요)는 다음 시간 모델링에서 제대로 파봅니다.
Step 7: "내 결과를 믿을 수 있나 — 우리 데이터셋과 검산 습관"
마지막으로, 앞으로 이 강의 내내 우리와 함께할 이 데이터에 대해 짧게 이야기할게요.
눈치채셨겠지만 우리 데이터셋은 일부러 작게 만들었어요. 회원 8명, 게시물 12개죠. 왜 이렇게 작을까요? 결과를 눈으로 셀 수 있게 하기 위해서예요.
"이 조회는 정확히 몇 건이 나와야 한다"를 미리 알 수 있으면, 결과가 맞는지 스스로 검산할 수 있어요. SELECT * FROM member 는 8건. 만약 7건이나 9건이 나오면 뭔가 잘못된 거죠. 그런데 데이터가 수백만 건이라면 이 검산이 불가능해집니다. "이 결과가 맞나?" 를 확인할 길이 없어지고, 배우는 사람은 자기가 짠 SQL 을 믿지 못하게 돼요.
그래서 우리 데이터에는 경계 사례 가 일부러 심어져 있어요.
- 게시물이 0개인 회원 — 7번(taeyang)·8번(narae)
- 자기소개(
bio)가 빈칸인 회원 — 3번·5번·8번
이런 "끝부분 사례"들이 나중에 더 복잡한 조회를 배울 때 함정을 정확히 짚어줍니다. 평범한 데이터만 있으면 놓칠 실수를, 이 경계 사례들이 잡아주거든요.
혹시 환경 잡기가 잘 안 되는 분들을 위해, 똑같은 데이터를 담은 공유 데이터셋(읽기 전용)도 안내합니다. 지난 시간에 본 무설치 경로처럼, 환경이 막혀도 강의를 따라올 수 있게 준비해뒀어요.
오늘부터 들일 습관 하나만 약속해요. 조회를 짤 때마다 "이건 몇 건 나올까?"를 먼저 예상하고 실행하기. 예상과 실제 결과를 맞춰보는 것, 그게 SQL 실력이 자라는 가장 빠른 길입니다.
💡 한 줄 정리
우리 데이터셋은 결과를 암산으로 검산할 수 있게 작게(회원 8·게시물 12) 만들었다. 조회 전에 "몇 건 나올까"를 먼저 예상하는 습관이 핵심이다.
🙋 학생 질문 — "튜터님, 데이터가 너무 적은 거 아니에요? 실무는 수백만 건일 텐데요."
맞아요, 실무 데이터는 어마어마하게 큽니다. 그런데 배울 때는 작은 데이터가 훨씬 좋아요.
이유는 방금 말한 검산 때문이에요. 결과가 8건이라고 예상했는데 정말 8건이 나오면, 내가 SQL 을 제대로 이해했다는 확신이 생기죠. 이렇게 문법과 원리를 작은 데이터로 확실히 다져두면, 데이터가 수백만 건으로 커져도 똑같은 SQL 이 그대로 동작합니다. SQL 문법은 데이터 크기와 상관없거든요.
물론 데이터가 커지면 "어떻게 하면 빠르게 조회할까" 같은 성능 고민이 새로 생기는데, 그건 기초를 탄탄히 쌓은 뒤 한참 나중에 만날 이야기예요. 지금은 작은 데이터로 원리를 즐기면 됩니다.
마무리
오늘 수고 많으셨어요! 지난 시간에 그냥 화면에 떴던 그 표가, 이제는 "테이블에 행과 열로 담긴 데이터"로 또렷하게 보이시죠?
오늘 배운 핵심 세 가지
- 💡 하나 — 관계형 데이터베이스는 데이터를 표(테이블) 모양으로 담는다. 표 전체가 테이블, 가로줄이 행(row), 세로 칸이 열(column)이다.
- 💡 둘 — 기본키(primary key)는 각 행을 유일하게 구분하는 열이다.
member표에서는member_id. 주민번호·학번 같은 역할을 한다. - 💡 셋 —
SELECT *는 모든 열,SELECT username, nickname은 원하는 열만 골라 조회한다. 회원과 게시물은member_id로 서로 연결된다.
다음 시간 예고
오늘 우리는 이미 만들어진 표를 들여다봤어요. 다음 시간엔 한 발 물러나서, "이 표들이 왜 이렇게 생겼는지"를 종이 위에서 직접 설계해봅니다. 회원·게시물·댓글·좋아요·팔로우 같은 인스타그램의 구성요소를 어떤 표로, 어떤 관계로 나눌지 그려보는 데이터 모델링 의 시작이에요. 오늘 맛본 기본키와 "표끼리 연결" 개념이 거기서 정식 이름(식별자·관계)을 얻습니다.
그리고 오늘 "원하는 열만" 골라봤듯이, "원하는 회원만"(특정 조건의 행만) 콕 집어 보는 조건 검색은 SQL 을 본격적으로 직접 짜기 시작하는 곳에서 깊게 다룹니다. 오늘 띄운 환경 그대로 이어가니, 컨테이너는 끄지 말고 그대로 두면 돼요.
과제
오늘 과제는 표를 직접 들여다보며 테이블·행·열·기본키 감각을 익히는 거예요. 작은 데이터라 결과를 눈으로 검산하기 좋습니다.
[기초] 표의 세 가지 이름 직접 확인하기
member 와 post 를 각각 SELECT * 로 조회하고, 각 표의 행이 몇 개인지(=몇 건인지) 세어보세요. member 는 8건, post 는 12건이 나오면 정답입니다. 그리고 각 표에 열이 몇 개이고 열 이름이 무엇인지도 적어보세요. (예: member 는 4열 — member_id, username, nickname, bio.)
[응용] 원하는 열만 골라 조회하기
post 표에서 post_id 와 caption 두 열만 골라 조회하는 SQL 을 직접 작성해 실행하세요. 결과가 12행·2열로 나오는지 확인합니다. 추가로 열 순서를 바꿔서(caption 을 먼저) 조회하면 결과가 어떻게 달라지는지도 비교해 보세요.
[심화] 기본키 후보 찾아보기
member 표에서 member_id 말고도 "각 회원을 유일하게 구분할 수 있을 것 같은" 열이 또 있을까요? 후보를 하나 찾아보고, 그 열을 기본키로 삼으면 어떤 점이 좋고 어떤 점이 불안한지(예: 값이 도중에 바뀔 수 있는지) 생각해서 적어보세요. 정답을 맞히는 것보다 "왜 그렇게 생각했는지"가 더 중요합니다.
생각해볼 주제
1. 왜 데이터를 '표' 모양으로 담을까?
세상의 데이터가 다 표로 딱 떨어지진 않아요. 예를 들어 친구의 친구로 끝없이 이어지는 인맥 관계나, 폴더 속의 폴더 같은 구조는 표 하나로 담기 어색합니다. 그런데도 가장 널리 쓰이는 데이터베이스는 여전히 '표(관계형)' 방식이에요. 표 모양이 주는 장점은 무엇이고, 반대로 표로 표현하기 어색한 데이터는 어떤 게 있을지 떠올려 보세요.
2. 기본키는 왜 '바뀌지 않는 값'이어야 할까?
회원을 구분하는 기본키로 username(아이디)을 쓰면 안 될까 잠깐 고민해봤죠. 만약 username 을 기본키로 정했는데 어떤 회원이 아이디를 바꾸고 싶어한다면 무슨 일이 벌어질까요? 그 회원의 게시물처럼 다른 데이터가 그 아이디를 가리키고 있었다면요? 기본키가 변할 때 생기는 연쇄 효과를 상상해 보세요.
3. 작은 데이터로 배우는 것의 가치
우리 데이터셋은 회원 8명짜리입니다. 실무 데이터는 수백만 건이죠. "결과를 눈으로 검산할 수 있다"는 것이 학습에서 왜 그렇게 중요할까요? 반대로, 작은 데이터로만 연습하면 놓치게 되는 것은 무엇일지도 함께 생각해 보세요.
✅ 예시 답안정답 보기
과제와 생각해볼 주제의 예시답안입니다. 과제는 "직접 조회해보고 결과를 검산"하는 데 초점이 있고, 생각해볼 주제는 자격증·실무 양쪽 관점을 함께 담았어요. 정답을 맞히는 것보다 "왜 그런지"를 설명할 수 있는지가 핵심입니다.
🎯 [과제 1 예시답안] 표의 세 가지 이름 직접 확인하기
채점 포인트
| 채점 포인트 | 확인 |
|---|---|
SELECT * 로 member·post 두 표를 각각 조회했는가 |
✅ |
행(건수)을 정확히 셌는가 — member 8건 · post 12건 |
✅ |
| 각 표의 열 개수·이름을 정확히 적었는가 — 둘 다 4열 | ✅ |
풀이 예시
SELECT * FROM member;
회원 8명이 조회됩니다. 열은 네 개 — member_id, username, nickname, bio.
SELECT * FROM post;
게시물 12개가 조회됩니다. 열은 네 개 — post_id, member_id, caption, created_at.
정리하면 이렇습니다.
| 테이블 | 행(건수) | 열 개수 | 열 이름 |
|---|---|---|---|
| member | 8 | 4 | member_id, username, nickname, bio |
| post | 12 | 4 | post_id, member_id, caption, created_at |
💡 튜터의 한마디
"행 = 건수"라는 감각이 핵심이에요. 조회 결과가 몇 줄인지가 곧 데이터가 몇 개인지죠. 앞으로 어떤 조회든 "몇 건 나왔나"를 먼저 확인하는 습관을 들이면, 결과가 맞는지 스스로 검산할 수 있습니다. 작은 데이터라 8건·12건을 눈으로 셀 수 있다는 점이 지금 단계의 큰 장점이에요.
🎯 [과제 2 예시답안] 원하는 열만 골라 조회하기
채점 포인트
| 채점 포인트 | 확인 |
|---|---|
SELECT post_id, caption FROM post 를 정확히 작성했는가 |
✅ |
| 결과가 12행·2열인지 확인했는가 | ✅ |
| 열 순서를 바꿔 조회하고 차이를 비교했는가 | ✅ |
풀이 예시
SELECT post_id, caption FROM post;
| post_id | caption |
|---|---|
| 1 | 첫 게시물입니다 #안녕 |
| 2 | 오늘도 코딩 #개발 |
| 3 | 주말 산책 |
| 4 | 아침 커피 한 잔 #카페 |
| ... | ... |
| 12 | 제주도 여행 #여행 |
12행이 나오고, 열은 post_id·caption 두 개뿐입니다. member_id·created_at 은 빠졌죠.
순서를 바꿔서 caption 을 먼저 적으면 이렇게 됩니다.
SELECT caption, post_id FROM post;
| caption | post_id |
|---|---|
| 첫 게시물입니다 #안녕 | 1 |
| 오늘도 코딩 #개발 | 2 |
| ... | ... |
| 제주도 여행 #여행 | 12 |
같은 데이터인데 열 순서만 바뀝니다. 행 수는 여전히 12 — 열을 고르고 순서를 바꿨을 뿐, 게시물을 거른 게 아니니까요.
💡 튜터의 한마디
두 가지를 확인했어요. 첫째, 열을 고르는 것은 행을 거르는 것과 다릅니다(행 수 12 그대로). 둘째, 적은 순서대로 결과 열이 나옵니다. 이 감각이 다음에 배울 정렬·조건 검색의 토대가 돼요.
🎯 [과제 3 예시답안] 기본키 후보 찾아보기
채점 포인트
| 채점 포인트 | 확인 |
|---|---|
username 을 후보로 찾았는가 (또는 합당한 근거의 다른 후보) |
✅ |
| 장점(겹치지 않음·비어있지 않음)을 설명했는가 | ✅ |
| 단점(값이 도중에 바뀔 수 있음)을 짚었는가 | ✅ |
풀이 예시
가장 유력한 후보는 username 입니다.
SELECT username FROM member;
결과를 쭉 훑어보면 jaehoon, minji … narae 처럼 겹치는 값이 하나도 없어요. 게다가 빈칸도 없죠. "겹치지 않고 비어있지 않다"는 기본키의 조건을 만족하니, 후보 자격이 충분합니다.
하지만 한 가지 불안이 있어요. username(아이디)은 사용자가 나중에 바꾸고 싶어할 수 있습니다. 누군가 아이디를 바꾸면, 그 회원을 가리키던 게시물(post.member_id)이 어긋나죠. 그래서 실제로는 절대 바뀌지 않을 숫자 member_id 를 기본키로 씁니다.
반대로 후보가 될 수 없는 열도 짚어볼까요? nickname(한글 이름)은 동명이인이 있을 수 있어 부적격, bio(자기소개)는 빈칸도 있고 겹칠 수도 있어 부적격입니다.
💡 튜터의 한마디
username 처럼 "원래 의미가 있어 자연스럽게 구분되는 값"과 member_id 처럼 "구분만을 위해 새로 부여한 숫자"는 성격이 다릅니다. 이 차이와 "기본키는 왜 안 바뀌어야 하나"는 다음 시간 모델링에서 정식으로 파봐요. 오늘은 후보를 찾고 장단점을 따져보는 사고 과정 자체가 정답입니다.
🤔 [생각해볼 주제 1] 왜 데이터를 '표' 모양으로 담을까?
문제 상황 요약
인맥 관계망이나 폴더 속 폴더처럼 표로 딱 떨어지지 않는 데이터도 많습니다. 그런데도 가장 널리 쓰이는 데이터베이스는 여전히 표(관계형) 방식이에요. 표의 장점은 무엇이고, 표로 어색한 데이터는 어떤 것일까요?
튜터의 가이드 및 해설
표가 주는 장점부터 봅시다.
- 정형성 — 모든 행이 같은 열 구조를 가져서 데이터가 가지런하고 예측 가능합니다.
- 명확성과 검증 — 몇 건인지, 어떤 값인지를 한눈에 세고 확인할 수 있어요.
- 표준 언어 — SQL 이라는 공통 언어로 누구나 같은 방식으로 다룹니다.
- 무결성 — 기본키나 중복 금지 같은 규칙으로 잘못된 데이터가 들어오는 걸 막습니다.
반대로 표로 담기 어색한 데이터도 있어요. 친구의 친구로 끝없이 이어지는 인맥 관계망은 그래프 형태가 자연스럽고, 폴더 속 폴더 같은 계층 구조는 트리 형태가 어울립니다. 이미지나 자유로운 문서 같은 비정형 데이터도 표에 억지로 담기 어렵죠.
그래서 세상에는 그래프 데이터베이스나 문서 데이터베이스 같은 다른 종류도 있습니다. 다만 회원·주문·결제처럼 정합성이 중요한 비즈니스 데이터는 대부분 표로 깔끔히 떨어지고, "틀린 데이터를 막는" 게 핵심이라 관계형이 여전히 토대예요. (관계형이 어디까지 진화했는지는 이 과목 맨 마지막에 살짝 구경합니다.)
🎯 SQLD는 이렇게 나온다
"관계형 데이터베이스의 특징"이 2과목의 첫 출발 개념으로 출제됩니다. 데이터를 표(릴레이션), 즉 행과 열로 구조화한다는 정의를 묻죠. 오답 함정은 "관계형이 모든 데이터에 항상 최선"이라는 식의 단정적 보기예요. 표로 어색한 데이터도 있다는 균형 감각이 필요합니다.
💡 실무에선
대부분의 서비스는 관계형 데이터베이스를 1순위로 깔고, 추천용 인맥 그래프나 대량 로그·세션처럼 특수한 데이터만 보조 데이터베이스로 곁들입니다. "기본은 관계형, 필요할 때 보조"가 현실적인 선택이에요.
🤔 [생각해볼 주제 2] 기본키는 왜 '바뀌지 않는 값'이어야 할까?
문제 상황 요약
회원을 구분하는 기본키로 username(아이디)을 쓰면 안 될까 고민해봤습니다. 만약 username 을 기본키로 정했는데 어떤 회원이 아이디를 바꾸고 싶어한다면, 그리고 게시물이 그 회원을 가리키고 있다면 무슨 일이 벌어질까요?
튜터의 가이드 및 해설
기본키가 바뀌면 어떤 일이 벌어지는지 따라가봅시다. post 표의 member_id 는 "누가 썼나"로 회원을 가리키고 있어요. 만약 회원의 기본키가 username 이고, 누군가 아이디를 minji 에서 minji_new 로 바꾼다면?
- 그 회원의 게시물은 여전히 옛 값
minji를 가리킴 → 연결이 끊깁니다. - 끊기지 않으려면 그 회원을 가리키던 모든 데이터를 함께 수정 해야 합니다.
- 한 군데라도 빠뜨리면 데이터가 깨집니다.
그래서 기본키는 "한 번 정하면 절대 안 바뀌는 값"이어야 해요. username 처럼 원래 의미가 있어 바뀔 여지가 있는 값보다, member_id 처럼 오직 구분만을 위해 부여한 숫자가 안전합니다. 앞엣것을 본질식별자, 뒤엣것을 인조식별자라고 부르는데, 정식 이름과 기준은 다음 시간에 다뤄요.
🎯 SQLD는 이렇게 나온다
주식별자(기본키)의 특성을 묻는 문제가 단골입니다. 유일성·최소성·불변성·존재성 네 가지인데, 그중 "값이 바뀌면 안 된다"는 불변성이 바로 이 주제예요. 본질식별자와 인조식별자를 구분하는 보기도 함께 출제됩니다.
💡 실무에선
거의 항상 자동으로 증가하는 숫자를 기본키로 씁니다. 주민번호나 이메일 같은 자연스러운 값은 바뀔 수 있고 개인정보·정책 이슈도 있어서, 기본키로는 피하는 게 정석이에요.
🤔 [생각해볼 주제 3] 작은 데이터로 배우는 것의 가치
문제 상황 요약
우리 데이터셋은 회원 8명짜리이고, 실무 데이터는 수백만 건입니다. "결과를 눈으로 검산할 수 있다"는 것이 학습에서 왜 중요할까요? 반대로 작은 데이터로만 연습하면 놓치는 것은 무엇일까요?
튜터의 가이드 및 해설
작은 데이터의 가장 큰 가치는 피드백 루프예요. "8건 나올 것"이라 예상하고 실행해서 정말 8건이 나오면, 내가 SQL 을 제대로 이해했다는 확신이 생깁니다. 결과가 어긋나면 바로 알아채고 고치죠. 데이터가 수백만 건이면 "이게 맞나?"를 확인할 길이 없어 이 루프가 끊깁니다.
대신 작은 데이터로만 연습하면 못 보는 것도 있어요.
- 성능 — 8건은 어떻게 짜도 빠르지만, 수백만 건에선 조회 방식에 따라 속도가 천차만별입니다.
- 대용량 함정 — 데이터가 클 때만 드러나는 비효율이 있어요.
- 동시성 — 여러 사람이 같은 데이터를 동시에 고칠 때의 문제도 작은 데이터에선 안 보입니다.
이런 건 기초를 탄탄히 쌓은 뒤 따로 만나요. 다행히 SQL 문법 자체는 데이터 크기와 무관해서, 작은 데이터로 익힌 SELECT 가 큰 데이터에서도 그대로 동작합니다.
🎯 SQLD는 이렇게 나온다
SQLD 는 "이 쿼리의 결과가 무엇인가"를 정확히 예측하는 문제가 핵심이에요. 작은 데이터로 결과 건수와 값을 손으로 세는 훈련이, 곧 시험에서 정답 보기를 분별하는 힘이 됩니다.
💡 실무에선
개발과 테스트는 작은 시드 데이터로 빠르게 돌리고, 성능은 따로 대용량 환경에서 측정합니다. 둘 다 필요해요 — 작은 데이터로 정확성을, 큰 데이터로 성능을 확인하는 거죠.