Skip to content

Commit fd78cd8

Browse files
authored
Merge pull request #155 from prgrms-aibe-devcourse/develop
약관추가
2 parents 147e563 + f124da2 commit fd78cd8

3 files changed

Lines changed: 121 additions & 0 deletions

File tree

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { useEffect, useRef } from 'react'
2+
3+
export default function TermsModal({ onClose }) {
4+
const onCloseRef = useRef(onClose)
5+
useEffect(() => { onCloseRef.current = onClose })
6+
7+
const dialogRef = useRef(null)
8+
9+
useEffect(() => {
10+
const prev = document.body.style.overflow
11+
document.body.style.overflow = 'hidden'
12+
dialogRef.current?.focus()
13+
const onKey = (e) => { if (e.key === 'Escape') onCloseRef.current() }
14+
window.addEventListener('keydown', onKey)
15+
return () => {
16+
document.body.style.overflow = prev
17+
window.removeEventListener('keydown', onKey)
18+
}
19+
}, [])
20+
21+
return (
22+
<div
23+
onClick={onClose}
24+
style={{
25+
position: 'fixed', inset: 0, zIndex: 1000,
26+
background: 'rgba(0,0,0,0.5)',
27+
display: 'flex', alignItems: 'center', justifyContent: 'center',
28+
padding: '16px',
29+
}}
30+
>
31+
<div
32+
ref={dialogRef}
33+
role="dialog"
34+
aria-modal="true"
35+
aria-labelledby="terms-modal-title"
36+
tabIndex={-1}
37+
onClick={(e) => e.stopPropagation()}
38+
style={{
39+
background: '#fff', borderRadius: 12, width: '100%', maxWidth: 640,
40+
maxHeight: '80vh', display: 'flex', flexDirection: 'column',
41+
boxShadow: '0 20px 60px rgba(0,0,0,0.2)',
42+
outline: 'none',
43+
}}
44+
>
45+
{/* 헤더 */}
46+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '20px 24px', borderBottom: '1px solid #e2e8f0' }}>
47+
<h2 id="terms-modal-title" style={{ margin: 0, fontSize: 17, fontWeight: 700 }}>이용약관 및 개인정보처리방침</h2>
48+
<button
49+
onClick={onClose}
50+
style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 20, color: '#94a3b8', lineHeight: 1, padding: '0 4px' }}
51+
aria-label="닫기"
52+
>
53+
54+
</button>
55+
</div>
56+
57+
{/* 본문 */}
58+
<div style={{ overflowY: 'auto', padding: '24px', lineHeight: 1.8, fontSize: 14, color: '#1e293b' }}>
59+
<section style={{ marginBottom: 28 }}>
60+
<h3 style={{ fontSize: 15, fontWeight: 700, marginBottom: 10, paddingBottom: 8, borderBottom: '1px solid #e2e8f0' }}>
61+
1. 개인정보 수집 및 이용 목적
62+
</h3>
63+
<ul style={{ paddingLeft: 20, margin: 0 }}>
64+
<li style={{ marginBottom: 6 }}>회원 가입 및 본인 식별 (이메일, 비밀번호, 이름, 성별, 생년월일, 역할)</li>
65+
<li style={{ marginBottom: 6 }}>소셜 로그인 최초 가입 시 추가 정보 수집 (성별, 생년월일, 휴대폰 번호(선택), 역할)</li>
66+
<li>마이페이지 내 사용자의 '로그인 기록 조회' 기능 제공 (접속 IP, 기기 정보)</li>
67+
</ul>
68+
</section>
69+
70+
<section style={{ marginBottom: 28 }}>
71+
<h3 style={{ fontSize: 15, fontWeight: 700, marginBottom: 10, paddingBottom: 8, borderBottom: '1px solid #e2e8f0' }}>
72+
2. 수집하는 개인정보 항목
73+
</h3>
74+
<p style={{ marginBottom: 8, fontWeight: 600 }}>사용자 입력 항목: 회원 ID, 비밀번호</p>
75+
<p style={{ marginBottom: 12, padding: '10px 14px', background: '#fffbeb', border: '1px solid #fde68a', borderRadius: 6 }}>
76+
⚠️ 비밀번호는 암호화 알고리즘(BCrypt)을 통해 안전하게 암호화되어 저장되므로 관리자도 열람할 수 없습니다.
77+
</p>
78+
<p style={{ margin: 0 }}>시스템 자동 수집 항목: 접속 일시, 접속 IP 주소, 기기 정보 (OS 및 브라우저 종류)</p>
79+
</section>
80+
81+
<section style={{ marginBottom: 28 }}>
82+
<h3 style={{ fontSize: 15, fontWeight: 700, marginBottom: 10, paddingBottom: 8, borderBottom: '1px solid #e2e8f0' }}>
83+
3. 개인정보의 보유 및 이용 기간
84+
</h3>
85+
<p style={{ marginBottom: 8 }}>
86+
수집된 모든 데이터는 본 토이 프로젝트의 테스트 및 평가가 완료되는 즉시 지체 없이 파기(데이터베이스 삭제)합니다.
87+
</p>
88+
<p style={{ margin: 0, fontWeight: 600 }}>파기 예정 시점: 2026년 7월 2일 (AIBE5 종료 시점)</p>
89+
</section>
90+
91+
<section>
92+
<h3 style={{ fontSize: 15, fontWeight: 700, marginBottom: 10, paddingBottom: 8, borderBottom: '1px solid #e2e8f0' }}>
93+
4. 이용자 유의사항
94+
</h3>
95+
<p style={{ margin: 0, padding: '12px 14px', background: '#fff1f2', border: '1px solid #fecdd3', borderRadius: 6 }}>
96+
본 사이트는 보안 테스트용 환경이므로, 타 사이트에서 사용하는 실제 비밀번호나 중요한 개인정보는 입력하지 마십시오. 가급적 테스트용 더미 계정 생성을 권장합니다.
97+
</p>
98+
</section>
99+
</div>
100+
101+
{/* 푸터 */}
102+
<div style={{ padding: '16px 24px', borderTop: '1px solid #e2e8f0', textAlign: 'right' }}>
103+
<button
104+
onClick={onClose}
105+
style={{ padding: '8px 28px', fontSize: 14, cursor: 'pointer', border: 'none', borderRadius: 8, background: '#3b82f6', color: '#fff', fontWeight: 600 }}
106+
>
107+
확인
108+
</button>
109+
</div>
110+
</div>
111+
</div>
112+
)
113+
}

src/pages/auth/LoginPage.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88
import { useState } from 'react'
99
import { Link, useNavigate } from 'react-router-dom'
10+
import TermsModal from '../../components/common/TermsModal.jsx'
1011
import { setAuthData } from '../../auth/tokenStore.js'
1112
import { API_BASE_URL } from '../../auth/authApi.js'
1213

@@ -38,6 +39,7 @@ export default function LoginPage() {
3839
const [passwordConfirm, setPasswordConfirm] = useState('')
3940
const [agreePolicies, setAgreePolicies] = useState(false)
4041
const [agreeMarketing, setAgreeMarketing] = useState(false)
42+
const [showTerms, setShowTerms] = useState(false)
4143
const [submitting, setSubmitting] = useState(false)
4244
const navigate = useNavigate()
4345

@@ -236,6 +238,7 @@ export default function LoginPage() {
236238

237239
return (
238240
<div className={isSignup ? 'mode-signup' : ''}>
241+
{showTerms && <TermsModal onClose={() => setShowTerms(false)} />}
239242
<div className="auth-header">
240243
<Link to="/" className="logo">
241244
<span className="logo-mark">S</span>
@@ -441,6 +444,7 @@ export default function LoginPage() {
441444
<label className="remember-me" style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12 }}>
442445
<input type="checkbox" name="agreePolicies" checked={agreePolicies} onChange={(e) => setAgreePolicies(e.target.checked)} />
443446
<span>이용약관 및 개인정보처리방침에 동의합니다. <strong>(필수)</strong></span>
447+
<button type="button" onClick={() => setShowTerms(true)} style={{ fontSize: 11, color: '#3b82f6', whiteSpace: 'nowrap', textDecoration: 'underline', background: 'none', border: 'none', cursor: 'pointer', padding: 0 }}>약관보기</button>
444448
</label>
445449
<label className="remember-me" style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, marginTop: 10 }}>
446450
<input type="checkbox" name="agreeMarketing" checked={agreeMarketing} onChange={(e) => setAgreeMarketing(e.target.checked)} />

src/pages/auth/OAuth2AdditionalInfoPage.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99
import { useState, useEffect } from 'react'
1010
import { useSearchParams, Link, useNavigate } from 'react-router-dom'
11+
import TermsModal from '../../components/common/TermsModal.jsx'
1112
import { setAuthData } from '../../auth/tokenStore.js'
1213
import { API_BASE_URL } from '../../auth/authApi.js'
1314

@@ -23,6 +24,7 @@ export default function OAuth2AdditionalInfoPage() {
2324
const [phone, setPhone] = useState('')
2425
const [agreePolicies, setAgreePolicies] = useState(false)
2526
const [agreeMarketing, setAgreeMarketing] = useState(false)
27+
const [showTerms, setShowTerms] = useState(false)
2628
const [submitting, setSubmitting] = useState(false)
2729
const [errorMsg, setErrorMsg] = useState('')
2830

@@ -145,6 +147,7 @@ export default function OAuth2AdditionalInfoPage() {
145147

146148
return (
147149
<div>
150+
{showTerms && <TermsModal onClose={() => setShowTerms(false)} />}
148151
<div className="auth-header">
149152
<Link to="/" className="logo">
150153
<span className="logo-mark">S</span>
@@ -236,6 +239,7 @@ export default function OAuth2AdditionalInfoPage() {
236239
<input type="checkbox" checked={agreePolicies}
237240
onChange={(e) => setAgreePolicies(e.target.checked)} />
238241
<span>이용약관 및 개인정보처리방침에 동의합니다. <strong>(필수)</strong></span>
242+
<button type="button" onClick={() => setShowTerms(true)} style={{ fontSize: 11, color: '#3b82f6', whiteSpace: 'nowrap', textDecoration: 'underline', background: 'none', border: 'none', cursor: 'pointer', padding: 0 }}>약관보기</button>
239243
</label>
240244
<label className="remember-me" style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12 }}>
241245
<input type="checkbox" checked={agreeMarketing}

0 commit comments

Comments
 (0)