반응형
📌 문제 상황
FastAPI에서 카테고리를 다음과 같은 구조로 저장한다고 가정하자.
- main_category: 대분류 (필수)
- sub_category: 중분류 (선택)
- sub_sub_category: 소분류 (선택)
DB 설계 시 sub_category, sub_sub_category는 nullable=True 로 되어 있고,
입력값이 없으면 None 혹은 ""(빈 문자열)로 들어올 수 있다.
하지만!
중복 체크를 할 때 "None"과 ""를 구분하지 않으면
실제로는 같은 카테고리인데 중복으로 인식되지 않는 문제가 발생할 수 있다.
⚠️ 실제 발생 가능한 예시
POST /categories
{
"main_category": "생활/건강",
"sub_category": "생활용품",
"sub_sub_category": ""
}
→ 이 경우 DB에는 sub_sub_category = ""로 저장됨.
POST /categories
{
"main_category": "생활/건강",
"sub_category": "생활용품"
}
→ 이 경우엔 sub_sub_category = null 로 들어감.
둘 다 소분류 없음을 의미하지만, 중복 체크 로직에서는 다르게 인식될 수 있다.
✅ 해결 방법
1. 빈 문자열을 None으로 변환하거나, None을 빈 문자열로 정규화한다.
이를 통해 비교 기준을 일관되게 만들 수 있다.
2. 정규화 유틸 함수 정의
def normalize_empty_str(value: Optional[str]) -> str:
"""None 또는 공백을 빈 문자열로 정규화"""
return value or ""
3. 중복 체크 함수에 적용
def is_category_already_exists(
db: Session,
main: str,
sub: Optional[str],
sub_sub: Optional[str]
) -> bool:
"""카테고리 중복 체크 (빈값 포함 비교 처리)"""
sub = normalize_empty_str(sub)
sub_sub = normalize_empty_str(sub_sub)
return (
db.query(Category)
.filter(
Category.main_category == main,
Category.sub_category == sub,
Category.sub_sub_category == sub_sub
)
.first()
is not None
)
4. 서비스 로직에서 심플하게 사용
if is_category_already_exists(
db,
data.main_category,
data.sub_category,
data.sub_sub_category,
):
raise AlreadyExistsException("이미 존재하는 카테고리입니다.")
✨ 정리 요약
구분 | 의미 | 처리 방법 |
None | 값이 아예 없음 | 빈 문자열로 변환하여 비교 |
"" | 공백 문자열 | None과 동일하게 처리 |
✔️ 해결책 | normalize 함수 사용 | 중복 체크 일관성 확보 |
✅ 결론
- null과 ""는 개발자가 보기엔 같지만, DB에선 다른 값이다.
- 중복 검사, 유효성 체크 시 빈값 처리를 명확하게 정규화해야 한다.
- 실수로 인한 중복 저장 방지를 위해, normalize 유틸을 적극 활용하자!
반응형
'Dev > Python' 카테고리의 다른 글
[FastAPI x PostgreSQL] 프로세스 정리 (with SQLAlchemy, Pydantic) (0) | 2025.04.08 |
---|---|
[FastAPI] dict vs Pydantic 스키마 사용 비교 (0) | 2025.04.07 |
[Python] 프로젝트에서 린트 & 포맷터 도입하는 이유와 셋업 방법 (flake8, black, isort, mypy, pre-commit) (0) | 2025.04.03 |
[FastAPI] Response vs FileResponse 차이와 사용법 (0) | 2025.04.02 |
[FastAPI] 페이징, 정렬, 리스트 파라미터까지 포함한 검색 API 확장 설계 (0) | 2025.03.31 |