다뤄야 할 내용
- 간단한 로그인/로그아웃 기능 구현 (비밀번호 없이 닉네임만 입력)
- 로그인 상태를 전역 상태(Context)로 관리
- 로그인 여부에 따라 글쓰기/댓글 작성 UI 제어
AuthContext 생성
src/
└── context/
└── AuthContext.jsx
AuthContext.jsx
import { createContext, useState, useEffect } from 'react';
export const AuthContext = createContext();
export function AuthProvider({ children }) {
const [user, setUser] = useState(() => {
const saved = localStorage.getItem('user');
return saved ? JSON.parse(saved) : null;
});
useEffect(() => {
localStorage.setItem('user', JSON.stringify(user));
}, [user]);
const login = nickname => {
setUser({ nickname });
};
const logout = () => {
setUser(null);
};
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
App.jsx에 Provider 적용
import { AuthProvider } from './context/AuthContext';
function App() {
return (
<AuthProvider>
<PostProvider>
<CommentProvider>
<Router />
</CommentProvider>
</PostProvider>
</AuthProvider>
);
}
로그인/로그아웃 UI 추가
pages/LoginPage.jsx
import { useState, useContext } from 'react';
import { AuthContext } from '../context/AuthContext';
import { useNavigate } from 'react-router-dom';
function LoginPage() {
const { login } = useContext(AuthContext);
const [nickname, setNickname] = useState('');
const navigate = useNavigate();
const handleLogin = e => {
e.preventDefault();
if (!nickname.trim()) return;
login(nickname);
navigate('/');
};
return (
<form onSubmit={handleLogin}>
<h2>로그인</h2>
<input
type="text"
placeholder="닉네임 입력"
value={nickname}
onChange={e => setNickname(e.target.value)}
/>
<button type="submit">로그인</button>
</form>
);
}
export default LoginPage;
라우터에 로그인 페이지 추가
import LoginPage from '../pages/LoginPage';
<Route path="/login" element={<LoginPage />} />
Header에 로그인 상태 표시
import { Link } from 'react-router-dom';
import { useContext } from 'react';
import { AuthContext } from '../context/AuthContext';
function Header() {
const { user, logout } = useContext(AuthContext);
return (
<header className="mb-6 border-b pb-4">
<h1 className="text-2xl font-bold mb-2">
<Link to="/">📝 My Blog</Link>
</h1>
<nav className="space-x-4">
<Link to="/">홈</Link>
<Link to="/write">글쓰기</Link>
{user ? (
<>
<span>{user.nickname}님</span>
<button onClick={logout}>로그아웃</button>
</>
) : (
<Link to="/login">로그인</Link>
)}
</nav>
</header>
);
}
export default Header;
댓글 입력 시 로그인 확인
PostPage.jsx 수정
import { useContext, useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { PostContext } from "../context/PostContext";
import { CommentContext } from "../context/CommentContext";
import PostForm from "../components/PostForm";
import { AuthContext } from "../context/AuthContext";
function PostPage() {
const { posts, updatePost, deletePost } = useContext(PostContext);
const { comments, addComment, deleteComment } = useContext(CommentContext);
const { user } = useContext(AuthContext);
const { id } = useParams();
const post = posts.find((p) => p.id === Number(id));
const navigate = useNavigate();
const [editing, setEditing] = useState(false);
const [commentText, setCommentText] = useState("");
if (!post) return <p>글을 찾을 수 없습니다.</p>;
const postComments = comments[post.id] || [];
return (
<div>
{editing ? (
<PostForm
initialTitle={post.title}
initialContent={post.content}
submitLabel="수정"
onSubmit={(title, content) => {
updatePost(post.id, title, content);
setEditing(false);
}}
/>
) : (
<>
<h2 className="text-2xl font-bold mb-2">{post.title}</h2>
<p className="mb-4 text-gray-700">{post.content}</p>
<button
className="bg-blue-500 text-white p-2 rounded-md mr-2"
onClick={() => setEditing(true)}
>
수정하기
</button>
<button
className="bg-red-500 text-white p-2 rounded-md"
onClick={() => {
deletePost(post.id);
navigate(-1);
}}
>
삭제하기
</button>
</>
)}
<hr />
<h3>댓글</h3>
<form
onSubmit={(e) => {
e.preventDefault();
if (!user) {
alert("로그인 후 이용해주세요.");
return;
}
if (!commentText.trim()) return;
addComment(post.id, commentText);
setCommentText("");
}}
>
<input
className="border border-gray-300 p-2 mb-2 w-full"
type="text"
placeholder="댓글을 입력하세요"
value={commentText}
onChange={(e) => setCommentText(e.target.value)}
/>
<button className="bg-blue-500 text-white p-2 rounded-md" type="submit">
등록
</button>
</form>
<ul>
{postComments.slice().reverse().map((c) => (
<li className="border-b py-2" key={c.id}>
{c.text}
<button className="bg-red-500 text-white p-2 rounded-md" onClick={() => deleteComment(post.id, c.id)}>삭제</button>
</li>
))}
</ul>
</div>
);
}
export default PostPage;
핵심1
const { user } = useContext(AuthContext);
<form
onSubmit={e => {
e.preventDefault();
if (!user) {
alert('로그인 후 댓글을 작성할 수 있습니다.');
return;
}
if (!commentText.trim()) return;
addComment(post.id, `${user.nickname}: ${commentText}`);
setCommentText('');
}}
>
반응형
'프로그래밍 > React' 카테고리의 다른 글
React로 블로그 만들기 - 댓글 전역 상태 관리 (CommentContext) (10) (0) | 2025.07.06 |
---|---|
React로 블로그 만들기 - 글 삭제 기능 추가하기 (9) (0) | 2025.07.06 |
React로 블로그 만들기 - 간단한 UI 구성 및 레이아웃 구성하기 (8) (0) | 2025.07.06 |
React로 블로그 만들기 - json-server 사용 (REST API mock) (7) (0) | 2025.07.06 |
React로 블로그 만들기 - 데이터 저장 유지 – localStorage (6) (0) | 2025.07.06 |