Skip to content

Conversation

@ayla-12
Copy link
Contributor

@ayla-12 ayla-12 commented Nov 13, 2024

✨ 구현 기능 명세

💡 기본 과제

  • React + TypeScript
  • Axios 라이브러리 사용
  • ThemeProvider, GlobalStyle 사용
  1. 로그인
  • 로그인 타이틀
  • 아이디(이름) 입력 Input
  • 비밀번호 입력 Input
  • 로그인 버튼 (hover시 배경색 바꾸기 (transition 적용))
  • 회원가입 버튼 (회원가입 페이지로 이동)
  1. 회원가입
  • 이름 - 비밀번호 - 취미 입력이 한 페이지에서 일어남 (컴포넌트만 갈아끼우기)
  • 상단에 회원가입 타이틀
  • 하단에는 로그인 페이지로 가는 링크
  1. 회원가입(이름)
  • 이름 입력 Input
  • 다음 버튼 (비밀번호 입력 폼 나옴)
  • Input 비어있을 때 버튼 비활성화
  1. 회원가입(비밀번호)
  • 비밀번호 입력 Input
  • 비밀번호 확인 Input
  • 둘 중 하나라도 비어있으면 버튼 비활성화
  • 두 비밀번호가 다르면 버튼 비활성화
  • 다음 버튼 (취미 입력 폼 나옴)
  1. 회원가입(취미)
  • 취미 입력 Input
  • 회원가입 버튼
  • Input 비어있을 때 버튼 비활성화
  • 회원가입 실패 시 에러메시지 alert 출력
  • 회원가입 성공 시 회원번호 alert 출력하고, login 페이지로 이동
  1. 마이페이지
  • 헤더에 취미, 내 정보 메뉴 탭
  • 헤더에 로그아웃 버튼
  • 로그아웃 버튼 클릭 시 token 저장 정보 삭제하고 로그인 페이지로 이동 (token 저장 위치는 자율)
  • 헤더 취미, 내 정보 취미 페이지, 내 정보 페이지 출력 (1개의 페이지로 구현해도 되고, url 달라도 됨)
  1. 마이페이지(취미)
  • 나의 취미 출력
  • 사용자 번호 입력 Input
  • 검색 버튼
  • 검색 오류시 alert
  • 검색된 취미 출력
  1. 마이페이지(내 정보)
  • 비밀번호만 입력하면 비밀번호만 변경
  • 취미만 입력하면 취미만 변경
  • 둘 다 입력하면 둘다 변경
  • 둘 다 비어있으면 alert

🔥 심화 과제

  • any 사용하지 않기
  1. 회원가입 (이름)
  • 8글자 넘어가도 버튼 비활성화 처리
  • 8글자 넘어가는 것에 대해 에러메시지 출력
  1. 회원가입 (비밀번호)
  • 비밀번호 보이기 버튼 추가
  • 8글자 넘어가도 버튼 비활성화 처리
  • 8글자 넘어가는 것에 대해 에러메시지 출력
  • 비밀번호 불일치 에러 메시지 출력
  • (선택) 에러메시지 한개만 출력해도 됨 (우선순위는 알아서)
  1. 회원가입 (취미)
  • 8글자 넘어가도 버튼 비활성화 처리
  • 8글자 넘어가는 것에 대해 에러메시지 출력

공유과제

제목:

링크 첨부 :


❗️ 내가 새로 알게 된 점

  • axios를 활용하여 api를 호출하는 방법을 알게 되었습니다
  • 라우터!!! 페이지 왔다갔다 하는 거 알게 되었습니다
  • navigate와 link의 차이점을 알게 되었습니다
  • 타스... 타입 선언은 참 어렵구나

❓ 구현 과정에서의 어려웠던/고민했던 부분

  • 아직 심화구현을 하지 못했어요. textfield 공통 컴포넌트에 에러 상태를 만들어두긴 했는데 상황에 따라 에러를 띄우는 걸 어떻게 해야할지 고민중에 있습니다.

🥲 소요 시간

  • 35h

🖼️ 구현 결과물

https://youtu.be/N4VFOxytWy8

Copy link
Member

@KIMGEONHWI KIMGEONHWI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가연님 4주차 과제 고생 많았어요!
api 테스트를 위해 생성한 프로젝트는 삭제해주고, css에서 색상 적용할 때 통일성을 위해 사용되는 색상은 모두 theme에 정의하여 사용하면 더 좋을 것 같아요!
날이 많이 추워졌는데, 감기 조심하세요🌊


const handleLogin = async () => {
try {
const response = await axios.post("http://211.188.53.75:8080/login", {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p1: 현재 베이스 URL이 그대로 노출되어 있습니다.이와 같이 민감한 정보들은 보안을 고려할 때 코드에 직접 노출하지 않고, 환경 변수 파일 (.env)에 저장하여 사용하는 것이 좋습니다. 또한 .gitignore.env를 추가해주어서 git에도 올라가지 않도록 하는 것이 좋아요! 참고로 .env 파일은 프로젝트의 루트 디렉토리에 생성하면 됩니다.

.env

VITE_BASE_URL=http://211.188.53.75:8080

사용할 때

const response = await axios.post(`${import.meta.env.VITE_BASE_URL}/login`

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines +17 to +24
<BrowserRouter>
<Routes>
{/* 로그인 페이지 */}
<Route path="/" element={<LogIn />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/mypage" element={<MyPage />} />
</Routes>
</BrowserRouter>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2: 라우터 로직을 다음과 같이 Router.tsx파일로 분리하여 사용하면, 보다 역할을 명확히하고 체계적으로 관리할 수 있을 것 같습니다.

Suggested change
<BrowserRouter>
<Routes>
{/* 로그인 페이지 */}
<Route path="/" element={<LogIn />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/mypage" element={<MyPage />} />
</Routes>
</BrowserRouter>
<Router />

Router.tsx

import { Routes, Route, BrowserRouter } from 'react-router-dom';
import LogIn from './pages/LogIn';
import SignUp from './pages/SignUp';
import MyPage from './pages/MyPage';

const Router = () => (
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<LogIn />} />
      <Route path="/signup" element={<SignUp />} />
      <Route path="/mypage" element={<MyPage />} />
    </Routes>
  </BrowserRouter>
);

export default Router;

App.tsx

import { ThemeProvider } from '@emotion/react';
import theme from './styles/theme';
import GlobalStyles from './styles/GlobalStyle';
import Router from './Router';

function App() {
  return (
    <ThemeProvider theme={theme}>
      <GlobalStyles />
      <Router />
    </ThemeProvider>
  );
}

export default App;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<BrowserRouter>를 사용해서 라우팅을 구현해주셨네요!
그런데, 이 방식 대신 createBrowserRouter를 사용하는 것을 더 추천드리고 싶어요!

결론부터 말씀드리면, createBrowserRouter는 라우트 설정을 보다 명시적이고 구조적으로 작성할 수 있도록 도와주는 최신 API입니다.
또한, 코드가 더 직관적이고 관리하기 쉬워지는 장점이 있어요.

문제점

  1. 라우트 설정의 명시성 부족
    <BrowserRouter> 방식은 라우트와 컴포넌트들을 JSX 안에서 설정해야 하기 때문에, 라우트 구조가 복잡해질수록 관리가 어려워질 수 있습니다.

  2. 코드의 확장성과 유지보수
    createBrowserRouter는 라우트를 객체 형태로 정의하여 별도 파일로 분리하거나 재사용하기 쉬운 구조를 제공합니다. 이런 방식은 코드 확장성과 유지보수에 유리합니다.

따라서, createBrowserRouter를 활용하면 아래와 같은 방식으로 작성할 수 있어요:

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: '/',
    element: <Home />,
    children: [
      { path: 'about', element: <About /> },
      { path: 'contact', element: <Contact /> },
    ],
  },
]);

<RouterProvider router={router} />;

위와 같은 방식은 라우트 구성과 UI 구성이 분리되기 때문에 코드의 가독성과 확장성이 더 좋아집니다!

관련 공식 문서 한 번 읽어보시는 것도 추천드려요 😊

Comment on lines +16 to +18
color: #fff;
background-color: ${({ color, theme, active = true }) =>
active ? (color || '#007BFF') : theme.colors.gray};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2: 통일성을 위해서 프로젝트에 사용되는 색상은 모두 theme에 정의하여 사용하는 것이 어떨까요?

Comment on lines +177 to +179
<Caption>
이미 회원이신가요? <StyledLink to="/">로그인</StyledLink>
</Caption>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3: MainBtnTextFIeld를 컴포넌트로 분리하여 사용하는 것으로 보아, 공통적으로 사용되는 부분은 분리한 것으로 보이는데, Caption또한 step 1, 2, 3 모두에서 사용되므로 통일성을 위해 분리하여 사용하는 것은 어떨까요?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3: 현재 step === 1, step === 2, step === 3이 모두 한페이지에서 작성되어있는데, step별로 컴포넌트를 분리하여 가독성을 높이는건 어떨까요?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p1:MyPage에서 취미내 정보 버튼을 클릭 하였을 때, 마운트 되는 컴포넌트가 모두 작성 되어있는데 3주차 과제와 같이 조건부 렌더링을 적용해서 분리하여 사용하는 것이 가독성과 관심사 분리 차원에서 좋을거 같아요!

Comment on lines +5 to +8
color?: string;
hoverColor?: string;
activeColor?: string;
active? : boolean;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2: 현재 MainBtn을 사용하기 위해서 color, hoverColor, activeColor, active를 props로 받아서 스타일링 해주고 있는데, 이유가 따로 있을까요?? 현재 프로젝트에서는 MainBtn의 색상, 호버시 색상등등이 모두 동일한 props를 넘겨주어 사용되고 있는데, 내부에서 스타일링 해주는 것이 좋지 않을까 싶습니다.

margin-top: 0.5rem;
`;

const TextField: React.FC<TextFieldProps> = ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3: 최근에는 React.FC를 사용하지 않는 것이 더 선호되고, 반환 타입을 명시적으로 지정하는 방법이 더 많이 사용되고 있다고 해요.
https://velog.io/@geonhwi1014/ReactReact.FC-%EC%82%AC%EC%9A%A9-%EC%A7%80%EC%96%91%ED%95%B4%EC%95%BC%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0
위의 아티클은 React.FC 사용 지양해야하는 이유를 주제로 제가 작성한 아티클인데 한번 읽어보면 좋을 것 같아요!

Copy link

@minjeoong minjeoong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4주차도 너무 고생 많으셨습니다 !! 🍀🍀👏🏻👏🏻

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거는 지워도 될 것 같습니다!

Comment on lines +19 to +23
{/* 로그인 페이지 */}
<Route path="/" element={<LogIn />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/mypage" element={<MyPage />} />
</Routes>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3 : 4주차 세미나 내용 중에 Router 에 파트에 있는 부분인데

import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Home from "./pages/Home";
import Login from "./pages/Login";m

// 라우터 설정
const router = createBrowserRouter([
  {
    path: "/",
    element: <Home />,
  },
  {
    path: "/login",
    element: <Login />,
  },
]);

function App() {
  return (
    <>
      <RouterProvider router={router} />
    </>
  );
}

export default App;

요즘은 이렇게 쓰는 게 새로나온 방법이라고 하더라구요!
참고만 해주시면 될 것 같아요!
documentation 걸어두겠습니당 -> https://reactrouter.com/en/main/routers/create-browser-router

Comment on lines +5 to +13
interface TextFieldProps {
type?: 'text' | 'password';
placeholder?: string;
value?: string; // 입력 필드 값
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void; // 변경 핸들러
error?: boolean; // 에러 상태
hint?: string; // 힌트 메시지
showToggleIcon?: boolean; // 비밀번호 가시성 토글 아이콘
}
Copy link

@minjeoong minjeoong Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2: ? <- 물음표를 모든 값에 주셨는데,
꼭 필요한 값은 에러로 잡아줘야 되기 때문에 되도록 안 붙이도록 하는게 좋다고 해요
예를들어 input 이면 value 나 type 은 꼭 필요할 수 있으니 ? 를 빼는 것이 좋을 것 같아요!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p5: 동의해요! 원하는 값을 렌더링하고 싶은데, ?로 처리를 해두면 빈 값도 에러가 잡히지 않아서 원하는 화면 구현이 어려울 것 같아요 🥹

});
const token = response.data.result.token;
localStorage.setItem("authToken", token); // 토큰 저장
console.log("로그인 성공!", response.data);
Copy link

@minjeoong minjeoong Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p5: 사용 후 콘솔은 제거하야해용!

const [userHobby, setUserHobby] = useState<string>("");
const [newPassword, setNewPassword] = useState<string>("");
const [newHobby, setNewHobby] = useState<string>("");
const [printNum, setPrintNum] = useState("");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3: 이것만 타입이 없습니다 !

"http://211.188.53.75:8080/user/my-hobby",
{
headers: { token: token }, // 'token'이라는 키로 헤더에 추가
}
Copy link

@minjeoong minjeoong Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p4: axios 를 초기세팅하는 방법이 있습니다!
axios 파일을 따로 만들어서, baseUrl 을 설정하고
헤더 또한 그 곳에서 설정해주는 것이 가장 좋습니다 :)
예시로 짰던 코드 첨부하겠습니다!

const api = axios.create({
  baseURL: PATH_API.API_DOMAIN,
  headers: {
    'Content-Type': 'application/json',
  },
  // withCredentials:true, // 쿠키 cors 통신 설정
});

api.interceptors.request.use(
  (config) => {
    // 요청 전 처리
    const token = localStorage.getItem('accessToken');
    if (token) {
      config.headers.token = `${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error),
);

Copy link
Member

@gudusol gudusol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

바쁘신 와중에 4주차 과제 정말 고생 많으셨습니다!!

합세도 화이팅입니다!!

Comment on lines +17 to +24
<BrowserRouter>
<Routes>
{/* 로그인 페이지 */}
<Route path="/" element={<LogIn />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/mypage" element={<MyPage />} />
</Routes>
</BrowserRouter>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<BrowserRouter>를 사용해서 라우팅을 구현해주셨네요!
그런데, 이 방식 대신 createBrowserRouter를 사용하는 것을 더 추천드리고 싶어요!

결론부터 말씀드리면, createBrowserRouter는 라우트 설정을 보다 명시적이고 구조적으로 작성할 수 있도록 도와주는 최신 API입니다.
또한, 코드가 더 직관적이고 관리하기 쉬워지는 장점이 있어요.

문제점

  1. 라우트 설정의 명시성 부족
    <BrowserRouter> 방식은 라우트와 컴포넌트들을 JSX 안에서 설정해야 하기 때문에, 라우트 구조가 복잡해질수록 관리가 어려워질 수 있습니다.

  2. 코드의 확장성과 유지보수
    createBrowserRouter는 라우트를 객체 형태로 정의하여 별도 파일로 분리하거나 재사용하기 쉬운 구조를 제공합니다. 이런 방식은 코드 확장성과 유지보수에 유리합니다.

따라서, createBrowserRouter를 활용하면 아래와 같은 방식으로 작성할 수 있어요:

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: '/',
    element: <Home />,
    children: [
      { path: 'about', element: <About /> },
      { path: 'contact', element: <Contact /> },
    ],
  },
]);

<RouterProvider router={router} />;

위와 같은 방식은 라우트 구성과 UI 구성이 분리되기 때문에 코드의 가독성과 확장성이 더 좋아집니다!

관련 공식 문서 한 번 읽어보시는 것도 추천드려요 😊

Comment on lines +107 to +110
<div>
<Container isVisible={step === 1}>
<Title>회원가입</Title>
<SubTitle>이름</SubTitle>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

최상단 div는 필요없는 div 인것 같습니다! <></> 로 감싸도 될 것 같습니다!


return (
<div>
<Container isVisible={step === 1}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

step 값에 따라 isVisible 속성으로 컨테이들이 보이고/안보이게 해주셨는데요!

{step === 1 && <Container isVisible={step === 1}> ... }

와 같이 조건에 따라 렌더링 여부를 결정해주면 조금 더 간단하지 않을까요?

혹시 style로 처리하신 이유가 따로 있으신가요?

Comment on lines +130 to +158
<Container isVisible={step === 2}>
<Title>회원가입</Title>
<SubTitle>비밀번호</SubTitle>
<TextField
type="password"
placeholder="비밀번호를 입력해주세요"
value={formValues.password}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleChange('password', e.target.value)}
showToggleIcon={true}
/>
<TextField
type="password"
placeholder="비밀번호 확인"
value={formValues.confirmPassword}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleChange('confirmPassword', e.target.value)}
/>
<MainBtn
color={theme.colors.primary_400}
hoverColor={theme.colors.primary_700}
activeColor={theme.colors.primary_700}
active={isNextButtonActive(2)}
onClick={handleNextStep}
>
다음
</MainBtn>
<Caption>
이미 회원이신가요? <StyledLink to="/">로그인</StyledLink>
</Caption>
</Container>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Container 안에 Title, SubTitle, Caption등이 반복해서 나오고 있는데,
Container를 컴포넌트로 분리할 수 있지않을까요?

조금씩 다르게 사용되는 본문 부분(TextField와 MainBtn 사용하는부분) 은 children으로 전달해주면 Container도 공통 컴포넌트로 만들 수 있을 것 같습니다!


const handleLogin = async () => {
try {
const response = await axios.post("http://211.188.53.75:8080/login", {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines +91 to +97
const [view, setView] = useState<string>("hobby"); // 보이는화면을 결정할 state
const [hobby, setHobby] = useState<string>(""); // API에서 받아온 취미 정보를 저장할 상태
const [userNum, setUserNum] = useState<string>(""); //검색을 위한 state
const [userHobby, setUserHobby] = useState<string>("");
const [newPassword, setNewPassword] = useState<string>("");
const [newHobby, setNewHobby] = useState<string>("");
const [printNum, setPrintNum] = useState("");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스터디때 말씀드렸던 것 처럼, 초기값이 있는 경우에는 명시적으로 타입을 지정해주지 않고, typescript가 타입을 추론할 수 있도록 해주는게 더 좋은 방식이라고 합니다!

Comment on lines +132 to +149
`http://211.188.53.75:8080/user/${userNum}/hobby`,
{
headers: { token: token },
}
);
console.log("다른 사람 취미 정보:", response.data);
setUserHobby(response.data.result.hobby); // 받아온 취미 정보를 상태에 저장
setPrintNum(userNum);
} catch (error) {
alert("취미 정보를 불러오는데 실패했습니다.");
console.error("취미 정보 요청 실패", error);
}
};

const postNewInformation = async () => {
try {
const response = await axios.put(
`http://211.188.53.75:8080/user`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이와 같이 API 요청을 보낼 때 마다, baseURL을 매번 적는 것은 비효율적이어 보입니다!
.env파일을 사용하더라도 매번 불러와야하구요!

(제가 안알려드렸지만) axios에는 instance라는 것이 있습니다! baseURL에 대해서 instance를 생성해두면, 뒤에 endpoint만 다르게 해서 요청을 보내는 것이 가능합니다!

문서 첨부해드릴테니 한 번 읽어보시면 좋을 것 같습니다! Axios 인스턴스

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 파일은 styled-components를 반환하는 단순 스타일 정의 파일로 보입니다.
따라서, 이 파일을 공통 컴포넌트로 분류하기보다는 "style 파일"로 보는 것이 더 적합할 것 같습니다.

components 폴더에 위치해있는 컴포넌트라면 TextField.tsx처럼 React Component를 반환하는 형태로 작성하는 것이 더 맞는 방식으로 보입니다.

/>
{showToggleIcon && type === 'password' && (
<ToggleIcon onClick={handleToggleVisibility}>
{isPasswordVisible ? '👁️' : '🙈'}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

귀엽네요

Comment on lines +13 to +21
"@emotion/css": "^11.13.4",
"@emotion/react": "^11.13.3",
"@emotion/style": "^0.8.0",
"@emotion/styled": "^11.13.0",
"axios": "^1.7.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.28.0",
"styled-components": "^6.1.13"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필요 없는 라이브러리들이 포함된 것 같습니다!

현재 styled 방식을 사용하며 @emotion/styled를 활용하고 있으니, @emotion/cssstyled-components는 필요하지 않을 것으로 보입니다.
또한, @emotion/style@emotion/styled를 설치할 때 발생한 오타로 설치된 라이브러리일 가능성이 높아 보입니다. 따라서, 이 라이브러리도 삭제해도 괜찮을 것 같습니다!

주의사항을 하나 드리자면, yarn add 를 통해 패키지를 설치하는건, 누군가가 만들어서 인터넷에 올려둔 라이브러리를 내 컴퓨터에 다운받는 행위입니다.
그렇기 때문에 잘못된 라이브러리르 설치하면 악성코드 등이 내컴퓨터에 설치될 수 있습니다.

따라서 npm 공식 홈페이지 등에서 download수, 유지보수 상태 등을 확인하고,
라이브러리 명을 직접 타이핑하기보다는(오타방지) 복사해와서 설치하는 것을 더 추천드립니다!

Copy link

@heesunee heesunee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

타입스크립트 사용에, Api 통신까지 많이 어려우셨을 것 같은데 너무 고생많으셨습니다ㅠㅠ 많은 분들이 좋은 코멘트 남겨주셔서 저는 비록 많은 리뷰를 남기지는 못했지만, 참고해서 리팩토링 하시면 더 좋은 코드가 될 것 같아요 ㅎㅎ 남은 합세도 화이팅!!

Comment on lines +5 to +13
interface TextFieldProps {
type?: 'text' | 'password';
placeholder?: string;
value?: string; // 입력 필드 값
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void; // 변경 핸들러
error?: boolean; // 에러 상태
hint?: string; // 힌트 메시지
showToggleIcon?: boolean; // 비밀번호 가시성 토글 아이콘
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p5: 동의해요! 원하는 값을 렌더링하고 싶은데, ?로 처리를 해두면 빈 값도 에러가 잡히지 않아서 원하는 화면 구현이 어려울 것 같아요 🥹

Comment on lines +15 to +17
const Container = styled.div`
margin-bottom: 1.5rem;
`;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p4: styled 형식으로 스타일링하다보면 모든 컴포넌트가 다 div로 작성될 가능성이 높은데요, 저는 전체 페이지에서 시맨틱 태그를 생각해두고 하는 편이에요! 텍스트필드도 하나의 section으로 볼 수 있으니까, styled.section으로 하는 것도 어떤지 한번 제안드려봅니다 ㅎㅎ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants