-
Notifications
You must be signed in to change notification settings - Fork 21
[조원정] sprint 9 #145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
gyulrangdev
merged 1 commit into
codeit-sprint-fullstack:next-조원정
from
jwj2087:next-조원정-sprint9
Oct 7, 2025
The head ref may contain hidden characters: "next-\uC870\uC6D0\uC815-sprint9"
Merged
[조원정] sprint 9 #145
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,21 @@ | ||
| /** @type {import('next').NextConfig} */ | ||
| const nextConfig = {}; | ||
| const nextConfig = { | ||
| images: { | ||
| remotePatterns: [ | ||
| { | ||
| protocol: "https", | ||
| hostname: "sprint-fe-project.s3.ap-northeast-2.amazonaws.com", | ||
| }, | ||
| { | ||
| protocol: "https", | ||
| hostname: "st.depositphotos.com", | ||
| }, | ||
| { | ||
| protocol: "https", | ||
| hostname: "i.pinimg.com", | ||
| }, | ||
| ], | ||
| }, | ||
| }; | ||
|
|
||
| export default nextConfig; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| const URL = `${process.env.NEXT_PUBLIC_CODEIT_URL}`; | ||
|
|
||
| // 회원가입 | ||
| export async function fetchSignUp({ | ||
| email, | ||
| nickname, | ||
| password, | ||
| passwordConfirmation, | ||
| }) { | ||
| const response = await fetch(`${URL}/auth/signUp`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| cache: "force-cache", | ||
| body: JSON.stringify({ | ||
| email, | ||
| nickname, | ||
| password, | ||
| passwordConfirmation, | ||
| }), | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| const errorText = await response.text(); // JSON이 아닐 수도 있으니 text로 | ||
| throw new Error(errorText); | ||
| } | ||
|
|
||
| return await response.json(); | ||
| } | ||
|
|
||
| // 로그인 | ||
| export async function fetchLogin({ email, password }) { | ||
| const response = await fetch(`${URL}/auth/signIn`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| cache: "force-cache", | ||
| body: JSON.stringify({ | ||
| email, | ||
| password, | ||
| }), | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| const errorData = await response.json(); | ||
| throw new Error("로그인에 실패했습니다."); | ||
| } | ||
|
|
||
| return await response.json(); | ||
| } | ||
|
|
||
| // get me | ||
| export async function fetchGetMe(token) { | ||
| const response = await fetch(`${URL}/users/me`, { | ||
| method: "GET", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| Authorization: `Bearer ${token}`, | ||
| }, | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| const errorData = await response.json(); | ||
| throw new Error("로그인에 실패했습니다."); | ||
| } | ||
|
|
||
| return await response.json(); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| const URL = `${process.env.NEXT_PUBLIC_CODEIT_URL}/products`; | ||
|
|
||
| // products list get | ||
| export const fetchProducts = async ({ | ||
| page = 1, | ||
| pageSize = 10, | ||
| orderBy = "recent", | ||
| }) => { | ||
| const response = await fetch( | ||
| `${URL}?page=${page}&pageSize=${pageSize}&orderBy=${orderBy}` | ||
| ); | ||
| if (!response.ok) { | ||
| throw new Error("서버에서 데이터를 가져오는데 실패했습니다."); | ||
| } | ||
| return await response.json(); | ||
| }; | ||
|
|
||
| // product get | ||
| export const fetchProduct = async ({ id }) => { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 용도별로 fetch를 따로 나눈 점이 좋습니다 👍 |
||
| const response = await fetch(`${URL}/${id}`); | ||
| if (!response.ok) { | ||
| throw new Error("서버에서 데이터를 가져오는데 실패했습니다."); | ||
| } | ||
| return await response.json(); | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import { cookieFetch } from "@/lib/fetchClient"; | ||
|
|
||
| // FormData 전용 fetch 함수 (Content-Type 헤더 없음) | ||
| const formDataFetch = async (url, options = {}) => { | ||
| const baseURL = process.env.NEXT_PUBLIC_API_URL; | ||
| const defaultOptions = { | ||
| // 쿠키 전송을 위한 설정 | ||
| credentials: "include", | ||
| // 서버 컴포넌트에서도 매번 재검증 | ||
| cache: "no-store", | ||
| }; | ||
|
|
||
| const mergedOptions = { | ||
| ...defaultOptions, | ||
| ...options, | ||
| }; | ||
|
|
||
| const response = await fetch(`${baseURL}${url}`, mergedOptions); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`API error: ${response.status}`); | ||
| } | ||
|
|
||
| try { | ||
| return await response.json(); | ||
| } catch (e) { | ||
| return { status: response.status, ok: response.ok }; | ||
| } | ||
| }; | ||
|
|
||
| export const userService = { | ||
| // 사용자 정보 요청 | ||
| getMe: () => cookieFetch("/users/me"), | ||
|
|
||
| // 사용자 링크 요청 | ||
| getMyLinks: () => cookieFetch("/users/me/links"), | ||
|
|
||
| // 사용자 정보 업데이트 (multipart/form-data) | ||
| updateMe: (formData) => | ||
| formDataFetch("/users/me", { | ||
| method: "PATCH", | ||
| body: formData, | ||
| }), | ||
|
|
||
| // 링크 삭제 | ||
| deleteLink: (linkId) => | ||
| cookieFetch(`/users/me/links/${linkId}`, { | ||
| method: "DELETE", | ||
| }), | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| "use client"; | ||
| import { AuthLogo } from "@/app/(components)/atoms/Logo"; | ||
| import OAuth from "@/app/(components)/atoms/OAuth"; | ||
| import PasswordInput from "@/app/(components)/atoms/PasswordInput"; | ||
| import TextInput from "@/app/(components)/atoms/TextInput"; | ||
| import Link from "next/link"; | ||
| import React, { useState } from "react"; | ||
| import { validateEmail, validatePassword } from "./validator"; | ||
| import { useAuth } from "@/providers/AuthProvider"; | ||
| import { useRouter } from "next/navigation"; | ||
|
|
||
| const LoginPage = () => { | ||
| const [value, setValue] = useState({ | ||
| email: "", | ||
| password: "", | ||
| }); | ||
| const [errors, setErrors] = useState({ email: "", password: "" }); | ||
| const { login } = useAuth(); | ||
| const router = useRouter(); | ||
|
|
||
| const handleEmailChange = (e) => { | ||
| const val = e.target.value; | ||
| setValue((prev) => ({ | ||
| ...prev, | ||
| email: val, | ||
| })); | ||
| setErrors((prev) => ({ ...prev, email: validateEmail(val) })); | ||
| }; | ||
|
|
||
| const handlePasswordChange = (e) => { | ||
| const val = e.target.value; | ||
| setValue((prev) => ({ | ||
| ...prev, | ||
| password: val, | ||
| })); | ||
| setErrors((prev) => ({ ...prev, password: validatePassword(val) })); | ||
| }; | ||
|
|
||
| // 버튼 활성화 조건 | ||
| const isFormValid = | ||
| value.email !== "" && | ||
| value.password !== "" && | ||
| !errors.email && | ||
| !errors.password; | ||
|
|
||
| const handleSubmit = async (e) => { | ||
| e.preventDefault(); | ||
|
|
||
| if (!isFormValid) { | ||
| console.log("invalidate"); | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| await login(value); | ||
|
|
||
| alert("로그인에 성공했습니다."); | ||
| router.push("/items"); | ||
| } catch (error) { | ||
| alert(error.message || "로그인에 실패했습니다."); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <> | ||
| <Link href="/"> | ||
| <AuthLogo /> | ||
| </Link> | ||
| <form onSubmit={handleSubmit} className="w-full flex flex-col gap-6"> | ||
| <TextInput | ||
| name="email" | ||
| title="이메일" | ||
| placeholder="이메일을 입력해주세요" | ||
| value={value.email} | ||
| onChange={handleEmailChange} | ||
| errmessage={errors.email} | ||
| isValid={!errors.email && value.email !== ""} | ||
| /> | ||
| <PasswordInput | ||
| name="password" | ||
| title="비밀번호" | ||
| placeholder="비밀번호를 입력해주세요" | ||
| value={value.password} | ||
| onChange={handlePasswordChange} | ||
| errmessage={errors.password} | ||
| isValid={!errors.password && value.password !== ""} | ||
| /> | ||
| <button | ||
| type="submit" | ||
| // disabled={isFormValid} | ||
| className={`w-full h-14 flex items-center justify-center rounded-4xl text-gray-100 text-xl font-semibold | ||
| ${isFormValid ? "bg-Primary-100 cursor-pointer" : "bg-gray-400"}`} | ||
| > | ||
| 로그인 | ||
| </button> | ||
| <OAuth /> | ||
| <div className="w-full text-center text-sm"> | ||
| <span className="text-gray-800 mr-1">판다마켓은 처음이신가요?</span> | ||
| <Link href="/signup"> | ||
| <span className="text-Primary-100 underline">회원가입</span> | ||
| </Link> | ||
| </div> | ||
| </form> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default LoginPage; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제 환경에서는 env 파일이 없어서 http://localhost:3000/undefined/auth/signIn 경로로 요청이 가서 정상적인 확인이 어렵네요 ㅠ
env 파일 자체는 레포에 올리지 않는 것은 맞습니다. 따라서 실무에서는 env.example 형태로 예시 파일을 추가하고 실제 env 정보는 DM으로 전달하거나 따로 문서로 관리하는 편입니다.
참고해주시면 좋겠습니다~