기능
User
회원가입 및 기본 인증
- 커스텀 유저 모델
- 기본 ID 대신 이메일을 고유 식별자(ID)로 사용하는 커스텀 유저 모델을 구현
- 이메일 인증 (Redis 활용)
- 회원가입 시 사용자가 입력한 이메일로 6자리 인증번호를 발송하며
- Redis를 이용해 5분 동안만 유효하도록 캐싱 처리하여 보안을 높임
- 회원가입 처리
- 이메일 인증이 완료된 사용자만 최종적으로 DB에 저장되며
- 이때 이메일 인증 캐시를 삭제하는 방어 로직이 적용
- JWT 로그인
- 이메일과 비밀번호로 로그인 시 Django 기본 인증을 거친 후
- Access Token과 Refresh Token을 발급하여 상태를 유지
소셜 로그인
- GitHub 및 Discord 로그인
- 외부 플랫폼을 통한 소셜 로그인 연동 기능이 구현
- 자동 닉네임 생성 및 연동
- 소셜 계정의 기본 닉네임이 기존 유저와 중복될 경우
- UUID를 활용해 랜덤한 영숫자를 덧붙여 고유한 닉네임을 자동으로 생성
- 자체 토큰 발급
- 소셜 인증이 완료되면 자체 DB에 계정을 연동/생성하고
- 프론트엔드에서 사용할 자체 JWT 토큰을 반환함
비밀번호 찾기 및 재설정
- 인증번호 발송
- 비밀번호를 잊어버린 유저를 위해 가입된 이메일로 6자리 재설정 인증번호를 발송
- 소셜 계정 방어 로직
- 소셜 로그인으로 가입한 유저가 비밀번호를 찾으려 할 경우
- "소셜 플랫폼으로 로그인해주세요"라는 예외 처리를 통해 에러를 방지
- 비밀번호 변경
- 인증번호가 일치하면 Django의 안전한 암호화 알고리즘(set_password)을 거쳐
- 새 비밀번호로 업데이트
프로필 관리
- 마이페이지 프로필
- 사용자의 이메일, 닉네임, 프로필 이미지 URL(S3), 한 줄 자기소개를 조회하고
- 수정(부분 수정 지원) 가능
- 닉네임 중복 검사
- 닉네임 변경이나 회원가입 시 기존 사용자와 닉네임이 겹치지 않는지
- 실시간으로 검사하는 기능 추가
- 타인 프로필 조회
- 닉네임을 기반으로 다른 유저의 공개된 프로필과 활동 통계 데이터를 조회 가능
유저 활동 통계 및 게이미피케이션
- 10단계 등급(레벨) 시스템
- 사용자가 작성한 정상 게시글(임시저장 제외) 수에 따라
- '희망찬 씨앗'(0개)부터 '울창한 생명의 숲'(500개)까지 10단계의 블로그 등급을 부여
- 진행률 계산
- 현재 등급뿐만 아니라
- 다음 레벨업까지 남은 게시글 수와 현재 진행률(%)을 동적으로 계산하여 보여줌
- 잔디밭(Heatmap) 데이터
- GitHub의 활동 기록처럼 유저가 글을 쓴 날짜들을 YYYY-M-D 형태의 데이터로
- 추출하여 프론트엔드의 잔디밭 그래프를 그릴 수 있게 제공
Post
게시글 작성 및 관리
- 게시글 CRUD 모델링
- 게시글의 생성, 조회, 수정, 삭제(CRUD)를 처리하는 핵심 로직들을
- 서비스 레이어(
post_create_service.py, post_manage_service.py)로 분리
- 썸네일 및 공개 여부 설정
- 데이터베이스 마이그레이션 기록을 볼 때, 게시글의 대표 썸네일을 지정하고
- 글의 상태를 공개/비공개(Visibility)로 설정할 수 있는 기능이 포함
- 시리즈 정렬
- 블로그의 특징인 '시리즈(연재글)' 기능을 지원함
- 시리즈 내에서 게시글의 순서를 지정하고 관리 가능
임시 저장 시스템
- 임시 저장 API 분리
- 작성 중인 글이 날아가는 것을 방지하기 위해 정식 발행 API와 분리된
- 임시 저장 전용 API(temp_post_api.py)를 구현함
미디어 파일 처리
- 본문 이미지 업로드
- 게시글 에디터 본문에 들어가는 이미지 파일들을 업로드하고
- URL을 반환받아 처리할 수 있는 독립적인 이미지 처리 API(image_api.py)가 구현
게시글 조회 및 필터링
- 목록 조회 서비스
- 방문자나 유저가 게시글 목록을 볼 때 사용되는 로직(post_list_service.py)으로
- 최신순/인기순 정렬이나 페이지네이션 등 목록 표시에 필요한 데이터를 가공하여 전달
- 상세 조회용 직렬화
- 게시글 목록용(List) 데이터와 게시글 상세 보기용(Detail) 데이터의 크기와 형태가 다르기 때문에
- 용도에 맞게 Serializer를 분리(post_list.py, post_detail.py)하여 네트워크 통신 효율을 높임
좋아요
- 좋아요 토글 기능
- 사용자가 마음에 드는 게시글에 좋아요를 남기고 취소할 수 있는
- 기능(post_like_api.py, post_like_service.py)을 지원
- 독립적인 Like 모델
- 유저와 게시물 간의 다대다(N:M) 관계를 관리하기 위해
- 중간 모델인 Like 모델(models/like.py)을 별도로 구축하여 데이터를 안전하게 저장
휴지통 시스템
- 소프트 딜리트(Soft Delete)
- 글을 삭제할 때 데이터베이스에서 완전히 날려버리는 대신
- 휴지통으로 이동시키는 기능(
trash_api.py, post_trash_service.py)을 통해
- 복구 및 영구 삭제
- 휴지통에 보관된 게시글을 다시 블로그로 복구하거나
- 필요 없는 경우 영구적으로 삭제할 수 있는 기능이 포함함
AI
AI 모델 연동 및 텍스트 변환
- 최신 Gemini API 연동
- 구글의 최신 공식 SDK인 google.genai를 도입
- 빠르고 성능이 뛰어난 gemini-2.5-flash 모델을 명시적으로 지정하여 텍스트 변환 로직을 구현
- 세밀한 생성 제어
- 생성된 텍스트가 블로그 글에 적합하도록 창의성(온도, temperature)을 0.7로 설정
- 긴 글도 잘리지 않도록 출력 토큰 제한(max_output_tokens)을 2500으로 넉넉하게 설정
다중 문체 프롬프트 시스템
- 다양한 문체 템플릿 지원
- 사용자의 취향과 블로그 성격에 맞게
- 'IT 전문가(논리적인 평어체)'
- '친근한 리뷰어(이모티콘과 존댓말)'
- '감성 에세이(서정적인 문체)'의 3가지 시스템 프롬프트를 정의했음
- 확장 가능한 매핑 구조
- 클라이언트가 요청하는 문체 키워드(professional, friendly, emotional)와
- 실제 프롬프트를 딕셔너리(TONE_MAPPING)로 매핑하여
- 추후 새로운 문체가 추가되더라도 코드 수정 없이 쉽게 확장할 수 있도록 설계
실시간 텍스트 스트리밍
- 제너레이터(Generator) 패턴
- AI 서버에서 응답이 전부 완성될 때까지 기다리지 않고
- 텍스트 조각(chunk)이 생성되는 즉시 UTF-8 바이트로 인코딩하여 반환(yield)
- 그리하여, 서버 대기 시간을 최소화
- StreamingHttpResponse 적용
- 프론트엔드 화면에서 마치 글자가 실시간으로 타이핑되는 듯한(ChatGPT와 같은) 기능을
- 제공하기 위해 StreamingHttpResponse를 도입
- 버퍼링 원천 차단
- 응답 타입(content_type)을 text/event-stream으로 설정하고
- 브라우저 캐싱 방지(Cache-Control: no-cache) 및 Nginx 등
- 웹 서버의 버퍼링을 막는 헤더(X-Accel-Buffering: no)를 꼼꼼하게 추가하여
- 지연 없는 매끄러운 스트리밍을 구현
Series
시리즈 데이터 모델링 및 무결성
- 유저 종속 모델링
- 시리즈를 생성한 유저(User 모델)와 외래키(ForeignKey)로 연결되어 있으며
- 유저 탈퇴 시 해당 유저의 시리즈도 함께 삭제(CASCADE)되도록 설계
- 중복 이름 방지(UniqueConstraint)
- 한 유저가 똑같은 이름의 시리즈를 여러 개 만들지 못하도록
- 데이터베이스 단에서 user와 name을 묶어
- 복합 고유 제약조건(Unique Constraint)을 걸어 데이터 무결성을 보장
시리즈 생성 및 관리 로직
- 비즈니스 로직 분리
- 시리즈를 생성, 수정, 삭제하는 핵심 로직들을 뷰(View)에 두지 않고
- series_manage_service.py라는 전용 서비스 계층으로 완벽히 분리하여 유지보수성 높임
- 권한 검증 및 방어
- 시리즈의 이름을 바꾸거나 삭제할 때
- 해당 시리즈의 ID와 요청을 보낸 유저 정보가 일치하는지
- 데이터베이스에서 먼저 검증하여 타인의 시리즈를 조작하지 못하도록 꼼꼼하게 차단
- DB 에러 핸들링
- 기존에 있는 시리즈 이름으로 생성이나 수정을 시도할 경우
- 발생하는 데이터베이스 에러(IntegrityError)를 포착하여
- 서버다운 없이 클라이언트에게 안전한 커스텀 예외 메시지로 전달
시리즈 목록 조회 및 최적화
- 최신순 목록 조회
- 본인이 생성한 시리즈 목록만 필터링하여 가져오며
- 항상 가장 최근에 만든 시리즈가 먼저 보이도록 최신순(-created_at)으로 정렬하여 반환
- 목적별 시리얼라이저 분리
- 단순히 이름만 입력받는 '생성/수정용 직렬화 객체(SeriesCreateSerializer)'와
- ID, 생성일, 수정일 등 다양한 정보가 필요한 '목록 조회용 직렬화 객체(SeriesListSerializer)'를
안전한 API 설계
- 엔드포인트 및 권한 분리
- 전체 목록 조회 및 생성을 담당하는 뷰(SeriesAPIView)와
- 단일 시리즈의 수정 및 삭제를 담당하는 뷰(SeriesDetailAPIView)를 나누어 RESTful 설계 진행
- 인증 필수 처리
- IsAuthenticated 권한 클래스를 뷰 레벨에 적용하여
- 로그인하지 않은 사용자는 시리즈 관련 API에 아예 접근조차 할 수 없도록 보안을 적용
데이터 모델링 및 다대다(N:M) 관계 설정
- 독립적인 태그 모델
- 태그 이름 자체를 관리하는 Tag 모델을 정의하고
unique=True 속성을 부여하여 데이터베이스에 동일한 이름의 태그가 중복 생성 방지
- 명시적인 중간 테이블(PostTag)
- 게시글(Post)과 태그(Tag) 사이의 다대다(N:M) 관계를 관리하기 위해
- PostTag라는 중간 모델을 정의
- 복합 고유 제약조건 방어
- 하나의 게시글에 똑같은 태그가 두 번 이상 중복해서 달리지 않도록
- PostTag 모델에 post와 tag를 묶는 UniqueConstraint를 걸어 데이터 무결성을 보장
동적 쿼리 및 태그 통계 집계
- 복잡한 필터링 로직
- 태그별 게시글 수를 집계할 때
- Django의 Q 객체를 활용하여 임시 저장된 글(is_temp=False)과
- 휴지통에 들어간 글(deleted_at__isnull=True)을 통계에서 제외하는 정교한 필터링을 수행
- 상황에 따른 조건 분기
- 동일한 서비스 함수(get_tags_with_post_counts) 내에서
- 요청한 사람이 '로그인한 본인'일 경우에는 내 글 전체를 기준으로 집계하고
- '일반 방문자'일 경우에는 전체 공개(PUBLIC)된 글만 기준으로 집계하도록 로직을 분리
- 데이터베이스 최적화(annotate)
- Python 메모리에서 개수를 세는 대신
- 데이터베이스 단에서 Count와 annotate 기능을 사용해
- post_count라는 가상의 필드를 생성하여 성능을 최적화
- 정렬 및 빈 태그 숨김
- 게시글 수가 0개인 태그는 목록에서 제외하며
- 게시글이 많은 순서대로(내림차순) 먼저 보여주고 개수가 같다면 가나다순(오름차순)으로 정렬
API 엔드포인트 및 직렬화 설계
- 목적별 API 뷰 분리
- 전체 공개된 태그 구름(Tag Cloud)을 보여주기 위한
- 뷰(TagListAPIView, 누구나 접근 가능)와 내 관리자 페이지 등에서 볼 수 있는
- 내 태그 통계 뷰(MyTagListAPIView, 로그인 필수)를 명확히 분리하여 RESTful 설계
- 가상 필드 직렬화
- 데이터베이스 모델에는 없지만 annotate를 통해 동적으로 만들어낸
- post_count 필드를 클라이언트(프론트엔드)에 JSON으로 전달하기 위해
- 시리얼라이저에 serializers.IntegerField(read_only=True)로 명시하여 처리
데이터 모델링
- 관계 설정
- 댓글 모델(Comment)은
- 게시글(Post), 사용자(User) 모델과 각각 외래키(ForeignKey)로 연결되어 있으며
- 게시글이나 사용자가 삭제될 때 연관된 댓글도 함께 삭제되도록 CASCADE 옵션이 적용
댓글 목록 조회 및 성능 최적화
- 유효성 검증
- 댓글 목록을 불러오기 전
- 해당 게시글이 휴지통에 들어갔거나(소프트 딜리트) 삭제되지 않은
- 정상적인 글인지(deleted_at__isnull=True) 먼저 확인하여 에러를 방지
- N+1 문제 해결
- 댓글 목록을 가져올 때 각 댓글의 작성자(User) 정보를 매번 DB에 따로 묻지 않도록
- select_related("user")를 사용하여 한 번의 쿼리로
- 작성자 정보까지 조인(Join)해서 가져오는 훌륭한 최적화가 적용되어 있음
- 페이지네이션
- 하나의 게시글에 수백 개의 댓글이 달릴 경우를 대비해
- API 뷰 단에서 PostPageNumberPagination을 적용하여
- 댓글 목록을 일정 개수씩 끊어서(페이지네이션) 전달
댓글 작성 및 권한 제어
- 읽기/쓰기 권한 분리
- IsAuthenticatedOrReadOnly 권한 클래스를 사용하여
- 일반 방문자(비회원)는 댓글 목록을 볼 수만 있고
- 댓글 작성은 로그인한 유저만 가능하도록 라우팅 권한을 분리
- 독립된 서비스 로직
- 댓글을 생성할 때도 게시글의 유효성을 먼저 검사하고 생성하는 비즈니스 로직을
- create_comment_service.py로 분리하여 코드의 가독성을 높임
댓글 수정 및 삭제
- 철저한 작성자 검증
- 댓글을 수정하거나 삭제하기 전
- 요청을 보낸 유저(user)와 댓글을 작성한 유저(comment.user)가 일치하는지
- 비교하여 타인의 댓글을 조작할 수 없도록 철저히 차단
- 부분 업데이트 최적화
- 댓글 내용을 수정할 때 객체 전체를 저장(Save)하는 대신
update_fields=["content"]를 명시하여
- 데이터베이스에 오직 content 컬럼만 업데이트하는 쿼리를 날리도록 성능을 향상
- 트랜잭션(Transaction) 적용
- 수정과 삭제 서비스 로직에
@transaction.atomic 데코레이터를 부착하여
- 작업 도중 에러가 발생하더라도 데이터베이스 상태가 안전하게 롤백되도록 안정성을 더함
직렬화 객체 분리
- 목적에 맞는 필드 조작
- 프론트엔드에 댓글 목록을 내려줄 때
list_comment_serializer.py에서
source="user.nickname"과 source="user.profile_img"를 활용하여
User 모델에 있는 닉네임과 프로필 이미지를 댓글 JSON 응답 안에 깔끔하게 포함
코드 분석
User 📌

- 🔐 회원가입 및 이메일 인증 (Signup & Email Verification)
관련 파일: email_view.py, signup.py, email_service.py, signup_service.py
기능 설명:
단순히 정보만 받아 가입시키는 것이 아니라, 이메일 소유를 검증하는 기능이 포함되어 있습니다.
EmailSendView를 통해 사용자의 이메일로 인증번호를 발송하고, EmailVerifyView에서 이를 검증합니다.
인증이 완료된 사용자에 한해 SignupView를 통해 최종적으로 회원가입을 처리합니다. 보안과 데이터 무결성을 높이는 좋은 구조입니다.
- 🔑 로그인 및 소셜 로그인 (Login & Social Login)
관련 파일: login.py, social_login.py, login_service.py, social_login_service.py, social_account.py (Model)
기능 설명:
일반 로그인: 가입한 이메일과 비밀번호를 검증하여 JWT(또는 세션) 토큰을 발급하는 기본적인 로그인 처리를 담당합니다.
소셜 로그인: 깃허브(GitHub), 디스코드(Discord) 등 외부 OAuth 제공자를 이용한 로그인 기능을 지원합니다.
별도의 social_account 모델을 두어 하나의 유저가 여러 소셜 계정을 연동할 수 있도록 확장성 있게 설계되었습니다.
- 🛠️ 비밀번호 찾기/재설정 (Password Reset)
관련 파일: password_view.py, password_service.py
기능 설명:
사용자가 비밀번호를 분실했을 때 이메일 인증을 통해 비밀번호를 안전하게 재설정할 수 있는 기능입니다.
가입 시 사용했던 이메일 인증 로직과 유사하게, 인증번호 발송(PasswordResetRequestView) 후 인증번호와 새 비밀번호를 함께 받아 처리(PasswordResetConfirmView)합니다.
- 👤 프로필 관리 (Profile Management)
관련 파일: profile_view.py, profile_service.py, profile_serializer.py
기능 설명:
로그인한 사용자가 자신의 프로필 정보(닉네임, 프로필 이미지 등)를 조회하고 수정할 수 있는 기능입니다.
사용자 본인만 접근할 수 있도록 권한(Permission) 처리가 되어 있을 것으로 보입니다.
- 📈 유저 활동 통계 (User Statistics / Garden)
관련 파일: users_stat_view.py, users_stat_service.py
기능 설명:
단순한 회원 관리를 넘어, 사용자의 활동 내역을 시각화할 수 있도록 통계 데이터를 제공하는 기능입니다.
앞선 리뷰에서 보았듯 '정원(잔디밭) 활동 통계'라는 개념을 통해 깃허브의 커밋 기록처럼 사용자의 사이트 내 활동(글 작성, 댓글 등)을 추적하고 클라이언트에 전달해 줍니다. 서비스의 리텐션(재방문율)을 높이는 매력적인 기능입니다.