요즘IT
위시켓
최근 검색어
전체 삭제
최근 검색어가 없습니다.

IT 지식이 무엇보다 중요해진 요즘, 여러분은 어떻게 공부하고 있나요? 가장 먼저 눈길이 가는 건 다양한 IT 강의 영상일 겁니다. 강의를 제공하는 교육 기업들과 함께, 요즘IT에서 ‘IT 강의 시리즈’를 준비했습니다. 엄선한 교육 영상을 텍스트로 읽고 필요한 정보를 빠르게 가져가세요.

회원가입을 하면 원하는 문장을
저장할 수 있어요!

다음

회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!

확인

개발

CHAR vs. VARCHAR, 언제 어떻게 써야 할까?

년차,
어떤 스킬
,
어떤 직무
독자들이 봤을까요?
어떤 독자들이 봤는지 궁금하다면?
로그인

IT 지식이 무엇보다 중요해진 요즘, 여러분은 어떻게 공부하고 있나요? 가장 먼저 눈길이 가는 건 다양한 IT 강의 영상일 겁니다. 강의를 제공하는 교육 기업들과 함께, 요즘IT에서 ‘IT 강의 시리즈’를 준비했습니다. 엄선한 교육 영상을 텍스트로 읽고 필요한 정보를 빠르게 가져가세요.

 

이번 강의는 ‘Real MySQL’입니다. 당근마켓 인프라실 DB팀에서 DBA로 근무하는 이성욱, 백은빈 님이 강의를 맡았습니다. MySQL 8.0 버전을 중심으로 실무에 바로 적용할 수 있는 MySQL 활용법을 다루고 있죠. 전체 영상은 인프런에서 확인할 수 있습니다.


Real MySQL 시리즈

 

CHAR vs. VARCHAR, 언제 어떻게 써야 할까?

 

안녕하세요, Real MySQL 강의를 진행할 강사 이성욱입니다. DBA로서 MySQL 서버와 몽고DB, HBase와 카산드라(Cassandra)를 운영하며 얻은 배움과 경험을 모아 강의를 준비했습니다. 이번 강의로 MySQL 프로젝트를 수행하는 개발자 또는 데이터베이스 관리자 분들에게 지금 당장 필요한 지식과 경험을 전달하기 위해 노력했죠.

 

매일 개발할 때 고민해왔고 앞으로도 고민할 문제, 지금까지는 고민하지 않고 넘겼지만 이제부터 고민해야 할 문제를 다룰 예정입니다. 함께 공부하고 지금까지와는 다른 시각으로 SQL 쿼리를 작성해 보세요.

 

이번 글에서는 MySQL 서버에서 문자열 값을 저장하는 컬럼의 타입으로 캐릭터(이하 CHAR)와 바 캐릭터(이하 VARCHAR) 타입 중 어떤 것을 선택하는 것이 좋을지 살펴볼게요.

 

데이터 모델링: 논리 모델링과 물리 모델링

우선 이에 앞서 데이터 모델링의 역할 분담을 짚고 넘어 갈게요. 데이터 모델링 작업은 개념 모델링에서 논리 모델링, 그리고 물리 모델링 단계를 거칩니다. 많은 업종에서 데이터 모델이 복잡하지 않은 경우, 개념 모델링 단계를 생략하고 논리 모델링 단계와 물리 모델링 단계로 나뉘어서 진행하죠.

 

논리 모델링과 물리 모델링을 구분해서 비교해 보겠습니다.

 

 

논리 모델링은 업무에 대한 이해가 많이 필요합니다. 엔티티의 속성, 엔티티 간의 관계를 도출하고 정규화 작업 등 주로 업무적인 요건을 모델링에 적용하고요.

 

반면 물리 모델링은 사용하는 DBMS의 종류와 기능, 그리고 성능에 대한 이해를 필요로 합니다. 이 단계에서는 DBMS 종류별로 최적의 타입을 선정하고 데이터의 접근 패턴을 분석해서 인덱스 전략을 수립합니다. 반정규화 등 성능 관련 요소를 모델링에 적용하기도 해요.

 

물론 아무리 논리 수준의 모델링이라 하더라도 어느 정도 소프트웨어 개발에 대한 경험과 이해가 필요합니다. 따라서 순수하게 서비스 기획만 하는 사람이 모델링을 진행하기에는 어려움이 있죠. 그렇다고 데이터베이스 관리자(이하 DBA)가 논리 모델링을 혼자 진행하기에는 서비스에 대한 이해가 부족한 경우가 많고요.

 

DBA가 서비스의 요건을 모두 학습해 논리 모델링을 진행할 수도 있을 겁니다. 하지만 대규모 인터넷 업체의 경우, 소규모 프로젝트들이 매우 많기 때문에 DBA가 모든 서비스의 요건을 분석하고 논리 모델링을 준비한다는 것은 어려움이 있어 보입니다.

 

그래서 대다수 소규모 프로젝트의 데이터 모델링 작업은 서비스에 대한 이해도와 소프트웨어 개발에 대한 경험을 겸비한 소프트웨어 개발자가 맡습니다. 논리 모델링과 물리 모델링을 묶어 한 번에 진행하는 편이죠. 하지만 소프트웨어 개발자는 여전히 DBMS 서버에 대한 이해와 경험이 부족할 수 있습니다.

 

따라서 먼저 소프트웨어 개발자가 모델링을 하고, DBA가 물리 모델링 수준의 검토, 보완 작업을 하며 최종적으로 데이터 모델이 완성됩니다. DBA는 모델링을 검토할 때, 쿼리 튜닝을 위한 반정규화나 인덱스 전략을 짜고 테이블의 통합, 분리를 진행합니다. 또, 컬럼의 데이터 타입을 검토하는 중요한 작업도 있죠.

 

 

CHAR vs. VARCHAR

오늘은 이런 작업 가운데 컬럼의 타입, 그 중에서 정말 중요한 문자열 컬럼 타입의 선정에 대해 알아보겠습니다.

 

특히 MySQL 서버에서 문자열을 저장하는 타입 가운데 CHAR와 VARCHAR 타입의 장점과 단점, 그리고 용도에 대해서 한번 살펴볼게요. 특히 ‘고정 문자열은 CHAR, 가변 문자열은 VARCHAR’라는 기준이 정말 유효한지, MySQL 서버의 저장 방식을 살펴보며 확인하려고 합니다.

 

 

공통점 알아보기

두 타입 모두 문자열을 저장하는 역할을 합니다. 이때 저장할 수 있는 문자열의 길이는 바꿀 수 있으므로 그때그때 직접 명시해주면 돼요. 가끔 명시하는 숫자 값을 바이트 수로 생각하는 분들이 있는데요, MySQL 서버에서 CHAR와 VARCHAR 타입에 명시하는 숫자 값은 문자의 최대 저장 개수를 의미합니다.

 

그럼 실제 CHAR와 VARCHAR 타입의 컬럼이 사용하는 디스크 공간의 크기는 얼마나 될까요? CHAR와 VARCHAR 타입 컬럼 모두 어떤 문자 셋으로 정의했느냐에 따라 사용하는 저장 공간의 크기가 달라질 수 있습니다.

 

예를 들어 라틴1(Latin1) 문자셋을 사용하는 CHAR와 VARCHAR 타입 컬럼은 최대 10글자까지 저장하며 라틴1 문자만 저장할 수 있습니다. 따라서 최대 10바이트까지만 사용할 수 있죠.

 

반면 UTF8MB4 문자셋을 사용하는 CHAR와 VARCHAR 타입 컬럼 역시 10글자까지 저장할 수 있지만, 저장 공간은 최소 10바이트, 최대 40바이트까지 쓸 수 있습니다. UTF-8 문자셋은 가변 길이 문자셋입니다. 영어 알파벳 10글자가 저장되면 10바이트만, 한글 10글자를 저장하면 30바이트, 그리고 특정 이모지 문자의 경우에는 최대 40바이트까지 사용하죠.

 

차이점: 저장 공간 할당

그렇다면 CHAR와 VARCHAR 타입의 차이점은 무엇일까요?

 

VARCHAR 타입은 딱 저장된 문자의 길이 만큼만 저장 공간을 할당합니다. 반면 CHAR 타입은 저장되는 문자 값의 길이에 관계없이 최대로 설정된 크기만큼 공간을 할당해서 사용해요. 또, CHAR 타입은 최대 255 글자까지만 저장할 수 있는데 반해 VARCHAR 타입은 대략 16000 글자까지 저장할 수 있죠.

 

CHAR 타입은 저장된 값의 길이를 별도로 관리하지 않지만, VARCHAR 타입의 컬럼은 저장된 문자열 값의 실제 바이트 수를 관리하는 길이 저장 바이트가 있다는 차이도 있습니다. 이는 필요에 따라 1바이트에서 2바이트까지 사용할 수 있고요.

 

다만 여기까지 보면 CHAR 타입은 항상 저장된 값의 길이를 저장하지 않고 VARCHAR 타입만 길이를 저장하는 것처럼 보일 수 있는데요. 실제로는 CHAR 타입 중에서도 UTF8MB4와 같이 가변 길이 문자셋을 사용하는 경우에는 VARCHAR처럼 컬럼에 저장된 값의 길이를 같이 관리합니다. 주의가 필요한 부분이죠.

 

예시로 알아보기: Latin1

라틴1 캐릭터셋을 사용하는 CHAR 타입과 VARCHAR 타입에 ABCD라는 문자열을 저장한다고 합시다. 실제 디스크에 어떻게 저장되는지 간단히 그림으로 살펴볼게요.

 

 

fd2 컬럼은 CHAR(10)으로 정의된 컬럼인데요. 이 컬럼에 ABCD 4글자 알파벳만 저장해도 MySQL 서버는 10바이트의 공간을 미리 예약합니다. 이 10바이트 중 4바이트만 사용하고, 나머지 6바이트는 비워두는 방식을 사용해요.

 

그런데 fd2 컬럼이 VARCHAR(10) 타입으로 정의되면, MySQL 서버는 ABCD 저장을 위해 꼭 필요한 4 바이트만 할당해서 사용합니다. 대신 VARCHAR 타입의 경우, 문자열 값 ABCD 바로 앞에 실제 저장된 문자열의 바이트 길이를 저장하기 위해서 바이트를 하나 할당해 둔 것을 확인할 수 있어요. 여기서는 영문 알파벳 ABCD를 저장하므로 길이 바이트에는 4라는 수가 들어간 것을 확인할 수 있죠.

 

이미지에서는 VARCHAR 타입의 길이 저장 바이트가 컬럼 바로 앞에 저장된 것처럼 표시되어 있지만, 이는 설명의 편의를 위한 것입니다. 실제로는 더 복잡한 형태로 저장되어 있어요.

 

예시로 알아보기: UTF8MB4

UTF8MB4와 같은 가변 캐릭터 셋을 사용하는 경우, CHAR와 VARCHAR는 어떻게 저장 공간을 할당하는지도 살펴볼게요.

 

 

라틴1 문자셋은 고정 길이 문자셋인 반면 UTF8MB4는 가변 길이 문자셋입니다. 애초에 VARCHAR 타입의 경우 실제 문자셋과 관계없이 항상 꼭 필요한 공간만 할당해서 사용합니다. 문제는 CHAR 타입인데요. CHAR 타입에서는 미리 예약할 공간의 크기를 어떻게 계산할까요?

 

UTF8MB4 문자셋을 사용하는 경우, 한 글자가 최대 4바이트까지 공간을 사용할 수 있습니다. 그래서 얼핏 생각하면, CHAR 타입 컬럼은 최대 40바이트까지 공백을 예약할 것처럼 보이죠. 하지만 실제 MySQL 서버는 저장 공간을 예약할 때, 문자 갯수보다는 바이트 수, 즉 10의 공간만 예약해 두도록 작동해요.

 

예시 이미지를 볼까요? CHAR 타입 컬럼에 한글 두 글자를 저장하면 fd2 컬럼은 6바이트를 사용합니다. 한글은 글자마다 3바이트씩 사용하기 때문이죠. 이제 MySQL 서버는 fd2 컬럼에 4개의 빈 공백 공간을 예약하게 됩니다.

 

그렇다면 이 CHAR(10) 타입 컬럼에 ‘한글연습’이라는 네 글자를 저장하면 어떻게 될까요? 한글 네 글자를 저장하려면 실제 컬럼에는 12바이트의 저장 공간이 필요합니다. 그런데 여기에는 별도로 할당된 빈 공간이 없죠. 사용하는 공간이 CHAR(10) 컬럼 타입에 정의된 바이트 길이인 10보다 더 크니, 추가로 빈 공간을 더 할당해 두지 않은 것이에요.

 

이처럼 CHAR 타입이라 해도 UTF8MB4와 같은 가변 길이 캐릭터셋을 사용하는 경우에는 예약해 두는 빈 공간이 아예 없기도 합니다. 이런 특성으로 인해 가변 길이 문자셋을 사용하는 CHAR 타입이 VARCHAR와 비슷하게 작동하는 상황도 있습니다. 이를 기억해 주세요.

 

가변 길이 문자셋의 다른 점은 하나 더 있습니다. CHAR 타입이라 하더라도 실제 저장된 문자 값이 사용하는 바이트 수가 별도로 관리되어야 한다는 점인데요. 이미지의 fd2 컬럼이 CHAR 타입임에도 10과 12라는 숫자 값이 앞단에 표시되어 있는 것을 확인할 수가 있죠.

 

 

컬럼의 ‘진짜’ 용도

지금까지 CHAR와 VARCHAR 타입이 어떤 구조로 데이터를 저장하는지 살펴보았는데요. 이제 각각 컬럼들의 용도를 한번 살펴보도록 할게요.

 

 

고정 문자열은 CHAR, 가변 문자열은 VARCHAR?

제가 DBMS를 처음 시작할 때에는 주로 고정된 문자열을 저장하는 경우에는 CHAR 타입을, 길이가 가변인 문자열을 저장할 때에는 VARCHAR 타입을 사용해야 한다는 이야기를 많이 들었어요. 이런 이야기는 아직도 변함없이 많이 들리긴 하더라고요.

 

이 기준대로라면 주민등록번호를 저장하는 컬럼의 경우에는 CHAR(13) 타입을 선택해야 하는 거죠. 그런데 이때, CHAR(13)이 아니라 VARCHAR(13)을 사용하면 어떻게 될까요? 사실 CHAR나 VARCHAR 타입, 둘 중 뭘 선택해도 거의 차이가 없습니다. 물론 VARCHAR 타입의 경우에는 길이가 한 바이트 커지긴 하겠지만, 이 한 바이트로 얼마나 큰 차이가 만들어 질까요? 대충 생각해 봐도 서비스에는 거의 영향이 없겠죠.

 

그러니 고정 길이 문자열인 경우에는 CHAR를 사용해야 한다는 것 자체가 아무런 의미가 없는 기준입니다. 이 기준대로라면 CHAR는 아예 사용하지 말고 그냥 VARCHAR만 사용하라는 말이 될 수도 있는 거죠.

 

그래서 이 ‘고정된 문자열은 CHAR, 가변인 문자열은 VARCHAR’ 기준은 CHAR 타입의 장점을 최대한 활용할 수 있는 기준이 아닙니다. 앞서 확인했듯 CHAR와 VARCHAR의 가장 큰 차이는 공간을 미리 예약하는지, 아닌지 여부에요. CHAR 타입이 공간을 미리 예약해 둔다는 말은 다시, 공간의 낭비가 생긴다는 것을 의미하겠죠.

 

여러 케이스를 보며 어떤 방식으로 공간의 낭비가 생기는지 볼게요.

 

첫 번째 케이스, 저장되는 문자열의 최소/최대 길이 가변폭이 대략 100글자 정도인 상황입니다. 상대적으로 이렇게 큰 경우라면 CHAR 타입은 실제 값을 저장하지 않고 공백으로 채웁니다. 따라서 낭비하는 공간이 어떤 경우에는 100바이트까지 커질 수 있겠죠.

 

반면 두 번째 케이스는 저장되는 문자열의 최소/최대 길이 가변폭이 10글자 정도인 상황입니다. 이처럼 비교적 작은 경우에는 CHAR 타입의 공백으로 인한 낭비 공간이 최대 10바이트 정도에 불과합니다.

 

알려진 기준을 적용한다면 두 케이스 모두 CHAR 대신 VARCHAR를 선택해야겠죠. 물론 정말로 최적화를 따진다면, 길이가 가변일 때 CHAR 보다는 VARCHAR 타입을 적용해 한 바이트라도 아끼는 것이 좋을 수 있고요.

 

CHAR가 VARCHAR 보다 나을 때가 있을까?

그런데 미리 공백으로 채워둔 예약 공간이 나름 장점을 제공한다면 어떨까요? 몇십 바이트 정도의 낭비를 충분히 투자할 가치가 있을 때도 있다면요.

 

 

VARCHAR 타입을 사용하는 fd2 컬럼을 포함해 문자열 컬럼이 3개인 테이블 레코드가 있다고 가정해 볼게요.

 

이 그림에서는 fd2 컬럼이 VARCHAR 타입으로 정의가 되었고, ABCD라는 문자열을 인서트 했습니다. 곧 첫 번째 이미지처럼 fd2 컬럼의 값이 채워지게 되겠죠. 이 상태에서 fd2 컬럼이 길이가 한 글자 더 긴 ABCDE로 업데이트 될때, MySQL 서버는 이를 어떻게 처리할까요?

 

레코드의 길이가 바뀌었기 때문에 MySQL 서버는 원래 레코드가 저장되어 있던 위치에 인플레이스로 업데이트 할 수 없다는 걸 알아낼 겁니다. 곧 같은 데이터 페이지 내에서 15바이트 레코드를 저장할 수 있는 공간을 새로 찾아 옮기겠죠.

 

여기서는 다행히 데이터 페이지가 거의 비어 있었습니다. 그래서 레코드를 저장할 수 있는 공간을 어렵지 않게 찾았습니다. 서버는 두 번째 이미지처럼 기존 레코드에 삭제 표시를 하고 그 뒤의 바로 레코드를 이동시켜 ABCD라는 문자열을 저장했어요.

 

그러나 MySQL 서버의 각 데이터 페이지에는 꾸준히 레코드가 인서트, 업데이트, 그리고 딜리트될 겁니다. 구조도 계속 바뀌고요. 그러다 보면 16KB 데이터 페이지에서 레코드 한 건을 저장할 수 있는 빈 공간을 찾는 것이 조금씩 조금씩 더 어려워지게 되겠죠.

 

그러다가 어느 순간에는 빈 공간을 찾지 못하는 시점이 올 거예요. 결국 페이지의 레코드를 다시 컴팩션(Compaction)하는 작업을 한 다음에야, 비로소 새로 저장할 공간을 찾게 될 겁니다.

 

만약 이때 fd2 컬럼이 VARCHAR(10)이 아니라 CHAR(10)이었다면, 이를 어떻게 처리했을까요? MySQL 서버는 fd2 컬럼을 위해서 충분한 공간을 미리 예약해 두었습니다. 물론 공간 낭비가 있지만, 이렇게 데이터가 한 글자 더 늘어나는 업데이트가 있어도 레코드를 통째로 옮겨 쓰는 복잡한 과정을 거칠 필요가 없습니다.

 

VARCHAR가 아닌 CHAR를 선택해야 할 때

이제 아마도 VARCHAR 타입 대신 CHAR 타입을 사용해야 하는 상황을 다 눈치 챘을 것 같아요. 방금 본 것처럼 MySQL 서버에서 CHAR 타입은 레코드의 위치를 옮겨 적어야 하는 가능성을 낮춘다는 장점이 있습니다.

 

 

특히 저장 문자열의 가변 길이 폭이 좁고 자주 변경되는 컬럼이라면, VARCHAR보다는 CHAR 타입이 유용합니다. 데이터 페이지 관리 작업을 줄이며 자연스럽게 페이지의 프래그멘테이션(Fragmentation)을 최소화해주는 효과가 생기니까요. 결과적으로 데이터 페이지의 조각 모음 작업이 덜 필요해지는 거죠. 그러니 장기적으로 보면 다시 공간 절약 효과까지 얻을 수 있고요.

 

이런 효과는 CHAR 타입의 약점인 공간 낭비를 보완해 줍니다. 그 결과, 오히려 VARCHAR 대비 CHAR 타입이 공간을 더 절약하는 경우도 생길 수 있는 거죠.

 

 

마치며

만약 값의 길이가 바뀌면서 업데이트가 빈번하게 발생하는 경우라면 CHAR 타입을 써보세요. 공간을 미리 예약해주는 CHAR 타입보다 오히려 VARCHAR 타입의 공간 효율이 더 나빠질 가능성도 있으니까요. 컬럼의 값을 변경할 때마다 레코드 저장 공간 확보를 위해 MySQL 서버가 데이터 페이지 조각 모음 작업을 하면 어떨까요? 성능은 점점 떨어질 겁니다.

 

이제 CHAR와 VARCHAR 타입의 선정 기준에서 단순히 “값의 길이가 가변인 경우에는 CHAR 대신 VARCHAR를 사용한다”는 기준은 잊어버리세요. 값의 가변 길이 범위 폭이 좁고 자주 바뀌는 경우에는 VARCHAR보다는 CHAR 타입이 더 나은 선택일 수 있습니다. 특히 이 컬럼이 인덱스된 컬럼이라면 더더욱 CHAR 타입이 효율적일 가능성이 높을 겁니다.


원본 강의 보러가기 https://u.inf.run/4fss3D5

 

요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

좋아요

댓글

공유

공유

온라인 IT교육 플랫폼 인프런
8
명 알림 받는 중

작가 홈

온라인 IT교육 플랫폼 인프런
8
명 알림 받는 중
안녕하세요:)
온라인 IT 교육 플랫폼 인프런입니다.

인프런은 누구나 원하는 걸 배우며 지식을 나눌 수 있는 라이프타임 커리어 학습 플랫폼입니다. 3,500개 이상의 다양한 IT, 프로그래밍, 인공지능, 데이터, 마케팅, 디자인, 엑셀 실무 등 입문부터 실전까지 꼭 필요한 지식이 가득해요. 인프런에서 배우고 나누고 성장하세요!

인프런 바로가기 : www.inflearn.com

좋아요

댓글

스크랩

공유

공유

요즘IT가 PICK한 뉴스레터를 매주 목요일에 만나보세요

요즘IT가 PICK한 뉴스레터를
매주 목요일에 만나보세요

뉴스레터를 구독하려면 동의가 필요합니다.
https://auth.wishket.com/login