주니어 개발자를 위한 sql 필수 개념 5가지

Q&A 큐레이션

1. SQL 테이블 구성 도움 부탁드려요

안녕하세요, SQL 테이블을 구상하려고하는데, 어떤 방식이 더 좋을지 몰라서 질문합니다. 요구사항은 사용자의 부가 정보를 저장하면 되구요. 부가 정보는 이메일, 주소, 전화번호 등등이 있습니다. 1번 방법 (사진 참고): contactType과 content로 다양한 정보 유형을 받는다 2번 방법 (사진 참고): email, phone, address 등 고정된 값을 받는다 1번과 2번 중 어떤 방식이 더 좋을지 잘 모르겠어요. 1번 방법이 더 많은 정보의 유형을 저장할 수 있지만 조회 할때 좀 더 복잡할 것 같고 2번 방법은 저장하려는 정보 유형이 추가될 때마다 칼럼을 추가해줘야하지만 조회 할때 좀 덜 복잡한 것 같습니다. 혹시 이런 유형의 테이블을 설계할때 팁 같은게 있을까요?


답변

요구사항이 단순히 사용자의 contacts라는 정보만 저장하면 되는 것인가요? (부가 정보라고 하셨는데 테이블 명을 UserContacts로 하셔서요) 요구사항이 조금 더 명확해야 어떤 방식이 좋은지 알 수 있을 것 같아요. 예를 들면, - 필드 (이메일, 전화번호 등) 중복은 허용되는지 - contacts 정보만 저장되는 것인지 - 검색 (조회)할 때는 어떤 값들로 찾을 것인지, 어떤식으로 하는지 - 조회를 더 많이 하는지 생성/수정을 더 많이 하는지 - 조회나 생성/수정을 어느 시점에, 얼마나 많이/자주 하게 되는지 - 실 서비스의 기능을 위한 정보인지, 통계나 분석을 위한 정보인지 등등이요. 질문자님이 말씀하신 것처럼 1번 방법으로 하게 되면 조건들이 더 복잡할수는 있을것 같아요. 요구사항에 따라 다르겠지만 아마도 특정 contactType에 대한 조회가 필요하다면 계속 where 조건을 걸어주셔야 할 것 같아요. 2번은 조회 조건이 더 단순한게 맞을 것 같습니다. 결론은 1번인지 2번인지는 요구사항에 따라 다를 것 같지만 저라면 2번을 선택할 것 같습니다. 저는 설계하면서 "어 이거 좀 복잡해 지는 것 같은데?"라는 생각이 들면 KISS 원칙을 따라서 더 단순한 방법으로 설계하려고 합니다🙂

모두 보기

2. 관계형 DB 설계 시 카테고리 같은 것은 어떻게 저장하시나요?

안녕하세요. 이번에 기능을 구현하다가 문득 궁금해져서 질문납깁니다. 요구사항은 게시글에 카테고리가 추가되고, 해당 카테고리에 따른 게시글을 모아보는 페이지와 네비게이션을 생성하는 작업입니다. 네비게이션에서 게시글 카테고리를 클릭하면 해당 카테고리의 게시물들을 보여주는게 주 목적입니다. 게시글이 post로 MySQL에 저장되고 있는데 여기에 category라는 값을 추가하려고 하는데요. 질문은 카테고리를 어떤 형태로 저장하는게 좋은가 입니다. 기존 코드와 DB 설계 (다른 유사한 테이블)를 살펴보면 category라는 값을 숫자로 저장시키고 사용하는 서비스에서 각 숫자에 맞는 설명?타입을 선언하는 방식이더라구요. 예를 들면, DB에 category = 1로 저장된 post는 사용하는 서비스 (서버)에서 enum 또는 const로 선언해서 category = 1이면 PostCategory.Article 타입을 같는 형태입니다. 이후 사용자가 보게되는 프론트에서도 유사하게 category에 따른 카피를 보여주고 있어요. 특이사항이 있다면, 프론트에서 category에 따라 보여지는 카피는 수시로 바뀔 수 있을 것 같아요. 예를 들면, category = 1인 Article 타입이라면 카피 문구가 “논문”이라고 했을때, 나중에 카피가 “기사” 같은 것으로 바뀔 수 있어요. 이렇게 category 처럼 데이터의 속성이 잘 안바뀌는 값은 숫자로 정의하고 사용하는 위치마다 그에 맞게 타입을 선언하는게 좋을까요? 아니면 category를 DB에 만들때 문자열 형태로하고 “article”이라는 값을 바로 DB에 저장하는게 좋을까요?


답변

우선 질문 주신 상황을 제대로 이해했는지 헷갈려서 아래의 가정을 하고 답변을 드리겠습니다 - 기존 posts 테이블에 INT 또는 TINYINT 타입인 category를 추가한다 - DB에서 꺼낸 post 데이터의 category는 단순 숫자이며 (1, 2, 3), 사용하는 서비스에서 단순 숫자에 맞는 타입을 명시한다. ex) category가 1일때 type은 article - 프론트에서도 마찬가지로 category가 1일때 type이 article이고, article에 맞는 카피를 보여준다 - category 자체는 안 바뀌지만, 카피는 바뀔 수 있다. ex) category = 1이면 무조건 article이다. 다만, article일때 "논문"을 보여주다가 나중에 "기사"로 바뀔 수 있다 이런 상황에 답은 없는 것 같지만, 저라면 기존 코드를 작성하셨던 개발자분을 붙잡고 설계할 때 왜 그렇게 했는지 여쭤볼 것 같네요. 만약 해당 작업자분이 안계신다면, category에 string을 그대로 넣어도 되는지가 궁금하시겠죠. 한번 살펴볼까요 우선 category에 string 그대로 넣었을때 이점을 생각해보겠습니다. 1. category에 숫자가 있는것보다, category의 이름이 들어가 있는게 더 명시적이다 2. 불필요한 카테고리 타입 선언 코드를 줄 일 수 있다 그럼 구현하려면 어떻게 해야할까요? 단순히 posts의 category에 string으로 넣는게 진정 효율적일까요? 우선 제가 생각했을때 비효율적인 부분은 아래와 같습니다. - 같은 데이터가 중복됨 - posts가 많아지면 많아질수록 category를 저장하는 메모리도 계속 발생함. 더군다나 string이라면 기존 TINYINT 같은 것보다 메모리도 많이 먹음 위 문제를 해결하려면 어떻게 하는게 좋을까요? 저라면 PostCategories라는 테이블을 새로 만들 것 같네요. 그리고 posts 테이블에는 postCategoryId를 추가할 것 같아요. category의 수도 최소화하고 중복을 제거하는 거죠. 이렇게하면 또 다른 고려사항이 생깁니다. - 기존 posts 조회 로직에 JOIN이 필요할 수 도 있음. - 위 테이블을 만드는데 시간이 듦 여기서 장기적으로 봤을때 더 관리하기 용이한 방법은 PostCategories 테이블을 만드는 것이라고 생각합니다. 하지만 현재 작업 시간이 별로 없다면 저라면 기존 DB 설계 방식 (TINYINT)을 유지할 것 같아요. 이유는: 1. 장기적 관점을 생각하는 것보다 현재 기능을 빠르게 개발하는게 먼저라고 생각됨 2. 기존 설계 방식에 다른 동료 개발자 분들이 더 익숙함 (더 빨리 코드를 이해할 가능성 높음) 3. 기존 설계 방식에 다른 고려사항이 있을 수 있음 (이것 때문에 이전 작업자에게 설계 이유를 여쭤보는게 좋은데, 안 계신다면 고민이 좀 될 것 같아요) 사실 저도 DB 설계 경험이 많지는 않아서 순전히 "내가 작업을 한다면?"이라는 관점으로 접근해봤구요. 다른 좋은 방법이 있거나, 다른 답변자 분들이 더 좋은 답변을 달아주실것 같아서 참고 정도만 해주시면 될 것 같습니다 :)

모두 보기

3. group by를 사용한 후 가장 최신데이터를 가져오고 싶어요.

group by를 이용해서 간단한 쿼리를 만들고 있는데요. group by를 사용하면 같이 합쳐지는 데이터들은 전부 사라지나요??ㅜㅜ 사라지면 남는 데이터는 무엇인가요? count(*)을 이용해서 개수를 새고 가장 최신순으로 들어온 데이터를 먼저 가져오고 싶어서 order by createdAt desc를 사용해도 이미 있던 데이터는 최신순 정렬이 안되더라고요. 제 추측으로는 id 값을 보면 group by를 하면 다른 컬럼의 데이터는 id 값이 가장 낮은걸 가져오는 것 같아요.


답변

질문자님의 추측이 맞습니다 select * from table group by column 하면 해당 컬럼에서 같은것들은 중복이 제거되고 가장 id값이 낮은 것이 나옵니다. 이후에 order by를 해도 최신 데이터를 사용할 수가 없는 것입니다. 단순히 가장 최신데이터로 정렬하고 싶으신거면 select * from table group by column order by max(createdAt) desc 이렇게 사용하시면 됩니다. 그러면 각각 그룹핑된 데이터의 가장 최근날짜로 order by 합니다. 이때 테이블의 createdAt은 id 값이 가장 낮은 것이 나오기때문에.. 컬럼으로 보고싶으시면 max(createdAt)를 추가해주면 됩니다. select *, max(createdAt) from table group by column order by max(createdAt) desc

모두 보기

4. DB 저장 시 중복 체크 어떻게 하시나요?

Spring에 mssql을 사용한 사이트를 운영중인데 고객이 회원가입 할 때 가끔씩 요청이 중복되어 들어와서 중복 저장 되는 경우가 생깁니다. Insert 쿼리 전에 select 쿼리로 조회해서 동일한 아이디가 있으면 가입을 막는 코드는 넣어 두었는데 거의 동시에 요청이 들어오면 select 쿼리에서 걸러지지 않았습니다. DB의 회원 테이블 아이디 컬럼에 유니크 제약 조건을 걸어볼까 했는데 탈퇴한 회원의 경우 로우를 남겨놓고 탈퇴 여부 컬럼만 변경하는 구조라서 아이디 값이 유일하지는 않아 제약조건도 걸지 못하고 있습니다. select 쿼리와 insert 쿼리를 트랜젝션으로 묶어볼까 했는데 insert시에 키 생성(identity)하는 부분 때문인지 테이블 락이 걸려서 데드락이 발생하는 것 같아 묶지 못하였습니다. 보통 이런 식으로 DB에 특정 조건에서 중복을 막아야 하는 경우는 어떻게 처리하시나요?


답변

으흠. 저라면 탈퇴 회원을 별도 테이블로 관리하도록 로직을 변경하고 유니크를 걸 것 같습니다; 더 좋은 방법이 있는지는 잘 모르겠네요. 🙃

외 1개 답변

모두 보기

5. sql 결합 인덱스 질문입니다.

초보 개발자 입니다. 사수가 table을 생성하는 쿼리를 보시고 인덱스도 추가해 넣으라고 하셔서 열심히 구글링을 해보고 있습니다. create table생성 쿼리 마지막에 INDEX <인덱스이름> ( 칼럼명1, 칼럼명2, ... ) 을 넣으려고 하는데 칼럼에 무엇을 넣어야하나요? 제가 생성한 칼럼을 다 넣으면 되는 걸까요? 검색 속도를 높이려고 Index를 추가한다고 하는데 그냥 다 넣어주면 되는 건가요? 질문을 요약 정리하면 1. 컬럼에는 어떤것을 넣어야하나요? 2. 컬럼의 순서도 중요한가요?


답변

1. 컬럼에는 어떤것을 넣어야하나요? - 검색할 때 where절을 사용하는 컬럼을 넣으셔야합니다. where절을 사용하지 않는 무관한 컬럼을 사용한다면 인덱스의 효과가 없습니다. 2. 컬럼의 순서도 중요한가요? - 네 순서도 중요합니다. where절을 사용하는 컬럼의 순서로 입력하셔야합니다. where type=0 and title='test'; 이런식이라면 type, title을 입력하셔야합니다. 결론 해당 데이터가 어떻게 조회되는지 다시한번 살펴보시고 필요한 index를 추가하셔야합니다. 또한 검색 효율을 높이기 위해 index를 여러개 생성할 수도 있습니다. 하지만 무분별한 Index는 데이터를 쓰고, 수정하고 삭제하는것에 성능저하를 불러일으킬 수도 있으니 잘 설정하셔야합니다. 인덱스에 대한 내용: https://soyeon207.github.io/db/2021/07/06/index-theory.html 인덱스 컬럼 순서 관련 내용: https://khdscor.tistory.com/51

모두 보기