diff --git a/backend-core/config/settings.py b/backend-core/config/settings.py
index 6e387f4..204d67e 100644
--- a/backend-core/config/settings.py
+++ b/backend-core/config/settings.py
@@ -43,7 +43,7 @@
SECRET_KEY = env('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = True
+DEBUG = env('DEBUG')
env_allowed_hosts = os.environ.get("ALLOWED_HOSTS","")
diff --git a/backend-core/missions/views.py b/backend-core/missions/views.py
index a0cf91f..e3df2ba 100644
--- a/backend-core/missions/views.py
+++ b/backend-core/missions/views.py
@@ -492,6 +492,7 @@ def chat_room(request: HttpRequest, mission_id: int, room_id: int) -> HttpRespon
can_confirm_performer = is_author and mission.status == "PENDING_APPROVAL"
show_complete_btn = is_author and mission.status == "MATCHED"
blockable_user = {"id": other_user.id, "username": other_user.username} if other_user else None
+ mission_end = mission.status == "COMPLETED"
return render(
request,
@@ -507,6 +508,7 @@ def chat_room(request: HttpRequest, mission_id: int, room_id: int) -> HttpRespon
"show_complete_btn": show_complete_btn,
"blockable_user": blockable_user,
"other_user": other_user,
+ "mission_end" : mission_end
},
)
diff --git a/backend-core/static/chat/js/room.js b/backend-core/static/chat/js/room.js
index 3cd13e9..743001f 100644
--- a/backend-core/static/chat/js/room.js
+++ b/backend-core/static/chat/js/room.js
@@ -99,6 +99,11 @@ class ChatClient {
acceptBtn.addEventListener('click', () => this.acceptMission());
}
+ const reviewBtn = document.getElementById('reviewPageBtn');
+ if(reviewBtn) {
+ reviewBtn.addEventListener('click',() => window.location.href = `/api/users/review_page/${this.missionId}/`)
+ }
+
// 메시지 전송
if (this.sendBtn) {
this.sendBtn.addEventListener('click', () => this.sendMessage());
@@ -237,6 +242,7 @@ class ChatClient {
if (res.ok && data.success) {
alert(data.message || '미션이 완료되었습니다.');
if (btn) btn.remove();
+ window.location.href = `/api/users/review_page/${this.missionId}/`
} else {
alert(data.error || '미션 완료에 실패했습니다.');
btn.disabled = false;
diff --git a/backend-core/static/users/css/announcement.css b/backend-core/static/users/css/announcement.css
new file mode 100644
index 0000000..46ace1f
--- /dev/null
+++ b/backend-core/static/users/css/announcement.css
@@ -0,0 +1,130 @@
+/* 가로 393px 고정 및 모바일 인터페이스 최적화 */
+body {
+ margin: 0;
+ padding: 0;
+ background-color: #f8f9fa; /* 외부 배경은 차분하게 */
+ display: flex;
+ justify-content: center;
+ font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, system-ui, Roboto, sans-serif;
+ -webkit-font-smoothing: antialiased;
+}
+
+.container {
+ width: 393px; /* 팀장님의 물리적 규격 준수 */
+ min-height: 100vh;
+ background-color: #ffffff;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.05);
+}
+
+/* 상단 내비게이션 바: 수평 정렬 및 고정 */
+.nav-header {
+ display: flex;
+ align-items: center; /* 수직 중앙 정렬 */
+ padding: 16px 20px;
+ background-color: #ffffff;
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ border-bottom: 1px solid #f1f3f5;
+}
+
+#back-arrow {
+ text-decoration: none;
+ color: #333333;
+ font-size: 24px;
+ font-weight: 300;
+ margin-right: 12px; /* 타이틀과의 간격 확보 */
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+}
+
+.header-title {
+ font-size: 18px;
+ font-weight: 700;
+ color: #212529;
+ margin: 0;
+ letter-spacing: -0.5px;
+}
+
+/* 본문 콘텐츠 영역 */
+.content {
+ padding: 24px 20px;
+ flex: 1;
+}
+
+.intro-text {
+ font-size: 15px;
+ color: #495057;
+ line-height: 1.6;
+ margin-bottom: 24px;
+}
+
+/* 요약 박스: 강조 포인트 */
+.summary-box {
+ background-color: #f1f7ff; /* 연세 블루 톤 가미 */
+ border-radius: 12px;
+ padding: 18px;
+ margin-bottom: 32px;
+}
+
+.summary-title {
+ font-weight: 700;
+ font-size: 15px;
+ color: #004b95; /* 포인트 컬러 */
+ margin-bottom: 12px;
+ display: flex;
+ align-items: center;
+}
+
+.summary-box ul {
+ margin: 0;
+ padding-left: 18px;
+}
+
+.summary-box li {
+ font-size: 14px;
+ color: #343a40;
+ margin-bottom: 8px;
+}
+
+/* 상세 섹션 */
+.detail-section h2 {
+ font-size: 16px;
+ font-weight: 700;
+ color: #212529;
+ margin: 28px 0 12px 0;
+ border-left: 4px solid #004b95; /* 섹션 구분선 */
+ padding-left: 10px;
+}
+
+.detail-section p,
+.detail-section li {
+ font-size: 14px;
+ color: #6c757d;
+ line-height: 1.7;
+ margin: 8px 0;
+}
+
+.detail-section ul {
+ padding-left: 18px;
+}
+
+/* 푸터 영역 */
+footer {
+ padding: 40px 20px 30px;
+ text-align: center;
+ background-color: #f8f9fa;
+ border-top: 1px solid #f1f3f5;
+}
+
+footer p {
+ font-size: 12px;
+ color: #adb5bd;
+ margin: 4px 0;
+}
\ No newline at end of file
diff --git a/backend-core/static/users/css/signup.css b/backend-core/static/users/css/signup.css
index 1c4335e..661d043 100644
--- a/backend-core/static/users/css/signup.css
+++ b/backend-core/static/users/css/signup.css
@@ -100,6 +100,14 @@ input {
box-sizing: border-box;
}
+#password_condition {
+ font-size: 12px;
+ color: #667085;
+ margin-top: 8px;
+ margin-bottom: 0;
+ line-height: 1.5;
+}
+
/* 1. 프로필 이미지 업로드 섹션 (새로 추가) */
.profile-upload-group {
display: flex;
@@ -314,8 +322,7 @@ input {
}
/* 8. 이용약관: 하단으로 충분히 공간 띄움 */
-.signup-container::after {
- content: "회원가입 시 Uniquest의 이용약관 및 개인정보처리방침에 동의하는 것으로 간주됩니다.";
+#announcement {
display: block;
margin-top: 30px;
padding: 16px;
@@ -325,4 +332,8 @@ input {
color: #667085;
line-height: 1.6;
text-align: left;
+}
+
+#announcement a{
+ text-decoration: underline;
}
\ No newline at end of file
diff --git a/backend-core/static/users/js/review_page.js b/backend-core/static/users/js/review_page.js
index 52f7f56..1e0b0fb 100644
--- a/backend-core/static/users/js/review_page.js
+++ b/backend-core/static/users/js/review_page.js
@@ -141,7 +141,7 @@ async function send_info(){
if (response.ok){
const data = await response.json()
- console.log(data)
+ window.location.href = `/api/users/homepage/`
} else {
console.log('실패')
}
diff --git a/backend-core/staticfiles/chat/js/room.js b/backend-core/staticfiles/chat/js/room.js
index 3cd13e9..743001f 100644
--- a/backend-core/staticfiles/chat/js/room.js
+++ b/backend-core/staticfiles/chat/js/room.js
@@ -99,6 +99,11 @@ class ChatClient {
acceptBtn.addEventListener('click', () => this.acceptMission());
}
+ const reviewBtn = document.getElementById('reviewPageBtn');
+ if(reviewBtn) {
+ reviewBtn.addEventListener('click',() => window.location.href = `/api/users/review_page/${this.missionId}/`)
+ }
+
// 메시지 전송
if (this.sendBtn) {
this.sendBtn.addEventListener('click', () => this.sendMessage());
@@ -237,6 +242,7 @@ class ChatClient {
if (res.ok && data.success) {
alert(data.message || '미션이 완료되었습니다.');
if (btn) btn.remove();
+ window.location.href = `/api/users/review_page/${this.missionId}/`
} else {
alert(data.error || '미션 완료에 실패했습니다.');
btn.disabled = false;
diff --git a/backend-core/staticfiles/users/css/announcement.css b/backend-core/staticfiles/users/css/announcement.css
new file mode 100644
index 0000000..46ace1f
--- /dev/null
+++ b/backend-core/staticfiles/users/css/announcement.css
@@ -0,0 +1,130 @@
+/* 가로 393px 고정 및 모바일 인터페이스 최적화 */
+body {
+ margin: 0;
+ padding: 0;
+ background-color: #f8f9fa; /* 외부 배경은 차분하게 */
+ display: flex;
+ justify-content: center;
+ font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, system-ui, Roboto, sans-serif;
+ -webkit-font-smoothing: antialiased;
+}
+
+.container {
+ width: 393px; /* 팀장님의 물리적 규격 준수 */
+ min-height: 100vh;
+ background-color: #ffffff;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.05);
+}
+
+/* 상단 내비게이션 바: 수평 정렬 및 고정 */
+.nav-header {
+ display: flex;
+ align-items: center; /* 수직 중앙 정렬 */
+ padding: 16px 20px;
+ background-color: #ffffff;
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ border-bottom: 1px solid #f1f3f5;
+}
+
+#back-arrow {
+ text-decoration: none;
+ color: #333333;
+ font-size: 24px;
+ font-weight: 300;
+ margin-right: 12px; /* 타이틀과의 간격 확보 */
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+}
+
+.header-title {
+ font-size: 18px;
+ font-weight: 700;
+ color: #212529;
+ margin: 0;
+ letter-spacing: -0.5px;
+}
+
+/* 본문 콘텐츠 영역 */
+.content {
+ padding: 24px 20px;
+ flex: 1;
+}
+
+.intro-text {
+ font-size: 15px;
+ color: #495057;
+ line-height: 1.6;
+ margin-bottom: 24px;
+}
+
+/* 요약 박스: 강조 포인트 */
+.summary-box {
+ background-color: #f1f7ff; /* 연세 블루 톤 가미 */
+ border-radius: 12px;
+ padding: 18px;
+ margin-bottom: 32px;
+}
+
+.summary-title {
+ font-weight: 700;
+ font-size: 15px;
+ color: #004b95; /* 포인트 컬러 */
+ margin-bottom: 12px;
+ display: flex;
+ align-items: center;
+}
+
+.summary-box ul {
+ margin: 0;
+ padding-left: 18px;
+}
+
+.summary-box li {
+ font-size: 14px;
+ color: #343a40;
+ margin-bottom: 8px;
+}
+
+/* 상세 섹션 */
+.detail-section h2 {
+ font-size: 16px;
+ font-weight: 700;
+ color: #212529;
+ margin: 28px 0 12px 0;
+ border-left: 4px solid #004b95; /* 섹션 구분선 */
+ padding-left: 10px;
+}
+
+.detail-section p,
+.detail-section li {
+ font-size: 14px;
+ color: #6c757d;
+ line-height: 1.7;
+ margin: 8px 0;
+}
+
+.detail-section ul {
+ padding-left: 18px;
+}
+
+/* 푸터 영역 */
+footer {
+ padding: 40px 20px 30px;
+ text-align: center;
+ background-color: #f8f9fa;
+ border-top: 1px solid #f1f3f5;
+}
+
+footer p {
+ font-size: 12px;
+ color: #adb5bd;
+ margin: 4px 0;
+}
\ No newline at end of file
diff --git a/backend-core/staticfiles/users/css/signup.css b/backend-core/staticfiles/users/css/signup.css
index 1c4335e..661d043 100644
--- a/backend-core/staticfiles/users/css/signup.css
+++ b/backend-core/staticfiles/users/css/signup.css
@@ -100,6 +100,14 @@ input {
box-sizing: border-box;
}
+#password_condition {
+ font-size: 12px;
+ color: #667085;
+ margin-top: 8px;
+ margin-bottom: 0;
+ line-height: 1.5;
+}
+
/* 1. 프로필 이미지 업로드 섹션 (새로 추가) */
.profile-upload-group {
display: flex;
@@ -314,8 +322,7 @@ input {
}
/* 8. 이용약관: 하단으로 충분히 공간 띄움 */
-.signup-container::after {
- content: "회원가입 시 Uniquest의 이용약관 및 개인정보처리방침에 동의하는 것으로 간주됩니다.";
+#announcement {
display: block;
margin-top: 30px;
padding: 16px;
@@ -325,4 +332,8 @@ input {
color: #667085;
line-height: 1.6;
text-align: left;
+}
+
+#announcement a{
+ text-decoration: underline;
}
\ No newline at end of file
diff --git a/backend-core/staticfiles/users/js/review_page.js b/backend-core/staticfiles/users/js/review_page.js
index 52f7f56..1e0b0fb 100644
--- a/backend-core/staticfiles/users/js/review_page.js
+++ b/backend-core/staticfiles/users/js/review_page.js
@@ -141,7 +141,7 @@ async function send_info(){
if (response.ok){
const data = await response.json()
- console.log(data)
+ window.location.href = `/api/users/homepage/`
} else {
console.log('실패')
}
diff --git a/backend-core/templates/chat/room.html b/backend-core/templates/chat/room.html
index 8e23b5a..4c1cff8 100644
--- a/backend-core/templates/chat/room.html
+++ b/backend-core/templates/chat/room.html
@@ -62,6 +62,9 @@
{{ other_user.username|default:mission.author.username
{% if can_accept %}
미션 수락하기
{% endif %}
+ {% if mission_end %}
+ 리뷰 남기기
+ {% endif %}
{% endif %}
diff --git a/backend-core/templates/missions/mission_detail.html b/backend-core/templates/missions/mission_detail.html
index fc8343f..b2fd402 100644
--- a/backend-core/templates/missions/mission_detail.html
+++ b/backend-core/templates/missions/mission_detail.html
@@ -212,7 +212,7 @@
diff --git a/backend-core/templates/users/signup.html b/backend-core/templates/users/signup.html
index f179b68..457fbc1 100644
--- a/backend-core/templates/users/signup.html
+++ b/backend-core/templates/users/signup.html
@@ -33,6 +33,7 @@
회원가입
+
+
+ 회원가입 시 Uniquest의 이용약관 및 개인정보처리방침에 동의하는 것으로 간주됩니다.
+ 상세보기
+
+
{% endblock %}
diff --git a/backend-core/users/migrations/0008_alter_user_univ_email_alter_user_username.py b/backend-core/users/migrations/0008_alter_user_univ_email_alter_user_username.py
new file mode 100644
index 0000000..6f4e856
--- /dev/null
+++ b/backend-core/users/migrations/0008_alter_user_univ_email_alter_user_username.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.28 on 2026-02-15 13:01
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('users', '0007_alter_user_userphoto'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='user',
+ name='univ_email',
+ field=models.EmailField(blank=True, max_length=254, null=True, unique=True, verbose_name='학교 이메일'),
+ ),
+ migrations.AlterField(
+ model_name='user',
+ name='username',
+ field=models.CharField(blank=True, max_length=150, null=True),
+ ),
+ ]
diff --git a/backend-core/users/models.py b/backend-core/users/models.py
index 22329fc..5a9dc59 100644
--- a/backend-core/users/models.py
+++ b/backend-core/users/models.py
@@ -15,6 +15,12 @@ class User(AbstractUser):
"""
사용자 모델 (AbstractUser 상속)
"""
+ username = models.CharField(
+ unique=False,
+ max_length=150,
+ blank=True,
+ null=True
+ )
# 대학 정보 (FK)
university = models.ForeignKey(
University,
@@ -27,7 +33,15 @@ class User(AbstractUser):
# 인증 및 신뢰도
is_student_verified = models.BooleanField(default=False, verbose_name="학생 인증 여부")
- univ_email = models.EmailField(blank=True, null=True, verbose_name="학교 이메일")
+ univ_email = models.EmailField(blank=True,
+ null=True,
+ verbose_name="학교 이메일",
+ unique=True
+ )
+
+ USERNAME_FIELD = 'univ_email'
+
+ REQUIRED_FIELDS = ['username']
# 매너 온도 (기본 80도)
manner_score = models.FloatField(default=80, verbose_name="매너 온도")
diff --git a/backend-core/users/urls.py b/backend-core/users/urls.py
index e9e0bfe..2596631 100644
--- a/backend-core/users/urls.py
+++ b/backend-core/users/urls.py
@@ -10,7 +10,8 @@
# 1. 회원가입 (HTML 페이지 연결 삭제 -> API 연결)
path('signup/', views.signup_page,name='signup_view'),
path('signup/submit/', RegisterView.as_view(), name='signup'),
-
+ path('signup/announcement/', views.announcement_page, name='announcement'),
+
# 2. 이메일 인증
path('verify-email/', views.verify_email, name='verify-email'),
diff --git a/backend-core/users/views.py b/backend-core/users/views.py
index 6cb7e8a..a09813e 100644
--- a/backend-core/users/views.py
+++ b/backend-core/users/views.py
@@ -194,6 +194,9 @@ def get(self, request):
def login_page(request):
return render(request, 'users/login.html')
+#개인정보 수집 페이지
+def announcement_page(request):
+ return render(request,'users/announcement_page.html')
@method_decorator(csrf_exempt, name='dispatch')
class MyLoginView(APIView):
@@ -458,10 +461,9 @@ def change_password(request):
try:
target_user = User.objects.get(univ_email=email)
target_user.set_password(password)
- print(target_user.password)
target_user.save()
- cache.delete("reset_token_{reset_token}")
+ cache.delete(f"reset_token_{reset_token}")
return Response({"message": f"{target_user.username} 비밀번호가 성공적으로 변경되었습니다."}, status=200)
except User.DoesNotExist:
@@ -478,9 +480,12 @@ def render_review_page(request,mission_id):
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def render_review_page_info(request,mission_id):
- target_mission = Mission.objects.get(id=mission_id)
- target_user = target_mission.author
user = request.user
+ target_mission = Mission.objects.get(id=mission_id)
+ if (target_mission.author.username == user.username): # 내가 등록자 일 때
+ target_user = target_mission.helper
+ else:
+ target_user = target_mission.author
mission_name = target_mission.title
username = target_user.username
@@ -491,9 +496,13 @@ def render_review_page_info(request,mission_id):
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def review_json(request):
+ user = request.user
review_json = json.loads(request.body)
target_mission = Mission.objects.get(id=review_json['personal_key'])
- target_user = target_mission.author
+ if (target_mission.author.username == user.username): # 내가 등록자 일 때
+ target_user = target_mission.helper
+ else:
+ target_user = target_mission.author
target_user.review_datas.append(review_json)
target_user.save()
return Response({"status": "success", "message": target_user.username}, status=200)