Pre-Project 2주차 회고

강성일·2023년 8월 21일
1
post-thumbnail

🎤 7일차


회의

오전 회의 때, 백엔드 BE 팀원들은 벌써 '회원 관리에 필요한 API'를 완성시키는 단계였다.
부팀장님께서 고생이 좀 많으신 것 같다 ㅎㅎ.. 백엔드분들 체고에요 👏👏

이어서 프론트엔드도 이때까지 중간 상황을 정리하였고, 진도를 쭉 나가는 것도 좋지만
현재까지 만든 컴포넌트와 페이지를 레고처럼 조립하고 다시 나아가는 게 좋아보였다.

따라서 일단 오늘까지는 FE 분들에게 각자 하던 부분까지 마무리하고
내일은 서로 조립을 해보는 시간을 갖기로 했다. (내일 광복절인데 되려나 🇰🇷)

지금까지도 잔잔하게 협업을 함으로써 보이지 않던 것들이 보이기 시작한다.

예를 들어서 서로 헤더와 푸터 합쳐보니 겹치더라.. 와 같은 ㅎ..

물론 이 중간에 contents가 들어갈 예정이기에 문제는 전혀 되지 않지만,
혼자할 때는 보이지 않던 부분들이다! 이래서 협업 협업 하는군뇨 ㅎㅎ


Login 페이지 기능 구현

점심때까지 간단하게 로그인 페이지 틀을 완성했다.
원본 사이트를 참고하면서, 원래 구현하려던 피그마를 동시에 소화하려니 시간이 좀 소요됐다.

새로운 기획하는 느낌이라 진행하면서 흥미로웠다 😚

원본 사이트에서 Facebook, GitHub AOuth를 더 지원한다는 점과
까먹은 비밀번호를 찾아주는 기능 제외하고는 모두 오후에 구현 완료하였다.

  • input: focus
  • button: hover, active

와 같은 디테일 CSS도 놓치지 않았다.
모두 원본 사이트를 참고하면서 똑같이 만들었다.


GitHub

칸반 보드도 여전히 활성화 중이다.
추가로 팀원은 Main Page, 글 작성 Page 작업에 진행 중인 것을 볼 수 있다.

PR도 컨벤션도 모두 어려워하던 팀원들도 모두 으쌰으쌰해서 잘 지켜주고 있다. 체고입니다 🔥

마지막으로 오늘 작업 커밋 내용 두고 마무리하겠다.
원래 로그아웃 페이지도 만들어야 하는데, 8시인 지금 조금 늦은 조기퇴근할 것이다.

휴식은 생명 💪



🎤 8일차


🇰🇷 대한민국 만세

오늘은 생명을 되찾았다.
정말 쉬어버렸따 ㅎ.ㅎ

내일부터 다시 열심히 뛰자 !



🎤 9일차


오전

오전에 보니까 PR이 4개나 쌓여있었다.

바로 서로 FE끼리 얘기 나누면서, 화면 공유로 Merge하고 dev 브랜치를 띄워봤다.

첫 번째 걱정이었던 것은 사이드 바의 간격 조절인데, 현재 계획으로써는
questions 페이지부터 사이드 바가 노출되므로 아직 현재까지는 수정할 부분이 없었다.

questions 페이지는 현재 다른 팀원분이 개발 진행 중이므로 이는 추후에 수정할 부분이 될 것이다.

이후에는 로그인 페이지 라우팅이 정상적으로 구동하는지 보는 정도였으며, 문제가 없었다.


오후

어후.. 글을 쓰는 지금 시간은 새벽 12시이다.
사실 저 사진을 찍고 나서 후에도 PR이 3개가 더 쌓였다.

현 시각 PR 상황.. 팀 분들이 굉장히 열심히 잘하신다..
Open에는 드디어 메인 questions 페이지가 대기 중이며, 내일 오전에 Merge할 예정이다.

그럼 이 중 오늘 나의 역할이었던, Sign up 페이지에 대해서 먼저 알아보고 이것저것 풀어보겠다.


Sign-up 페이지 기능 구현

일단 issue는 다음과 같으며, 원래는 드롭다운과 reCAPTCHA 기능이 없었다.
하다보니까 욕심이 나서 시간을 조금 더 쏟게 되었다.

일단 여기까지가 1차 구현이었다.

로그인 컴포넌트를 재사용하여, 처음 틀을 잡는데에는
시간이 오래 걸리지 않았으나, 다른 쪽에서 시간이 오래 걸렸다.

reCAPTCHA 기능을 첨부하느냐고 라이브러리 깔고,
google 들어가서 키 설정하고 난리도 아니었다.

그러나 도메인이 현재로써는 localhost밖에 불가능하기에, 아직까지는 오류가 난다.
그래도 이러한 시도를 했다는 것을 남기기 위해, key 값을 환경변수로 만들어주고 .env 파일에 넣었다.

추후에 도메인이 완성되면, 그때 실제 데이터 값을 넣으면 되겠다.

다음은 드롭다운 기능이었다. 본문 옆에 있는 ? 버튼을 누르면 드롭다운이 나온다.

stackoverflow 원본 사이트인데, 나름 기능적인 면은 모두 갖췄기 때문에 만족한다.


간단한 Bug fix

회원가입 페이지도 로그인 페이지와 마찬가지로 컴포넌트 재사용으로 인해
높이를 고정으로 지정해줬었는데, 이 부분에서 브라우저 창을 줄이면 버그가 생겼다.

다음과 같이 고정 값에서 min-height로 변경하여 최소 높이를 보장했다.
따라서 브라우저 창을 줄여도 스크롤이 생기면서 컴포넌트가 뚫리는 현상이 생기지 않는다.

이건 또 다른 버그였는데, 한 팀원분이 버튼 재사용을 위해 컴포넌트로 따로 만들었다.

하지만 이전에 이미 병합했던 edit 페이지에선 반영이 되지 않아 버튼 수정한 부분이 빠지게 됐다.
따라서 기분이 나쁘시지 않게 사실을 전달하고, 허락을 받은 후 픽스했다.


FE 팀원들의 구현

questions postedit 사이트인데 정말 잘 만드셨다..
디자인 적으로도 뭔가 깔끔하면서 귀여운 느낌 😁

추가로 sidebar 컴포넌트의 윤곽을 볼 수 있다!

다들 너무너무 고생 많으시다 🔥


BE 팀원들의 구현

오늘 오전에 부팀장님께서 API 사양서가 벌써 완료됐다고 보여주셨다.
ㄷㄷ.. 굉장히 어려워보이는데 거의 하루 만에 해오신 듯 해보였다..

노력의 흔적이 고스란히 남겨있다.
굉장한 멋짐을 표현해드렸다 👍👍

따라서 내일 바로 FE팀은 로그인 로직 구현에 나가기로 했다.



🎤 10일차


🤯 머리 터진 오전

오전에 로그인 & 회원가입 로직 부분을 구현하기로 했다.
그런데 오전이 그냥 사라졌다.. 내가 뭘 해야될지 잘 모르겠는거다.

좋아. 로직 구현해. 근데 어떻게 해? 뭐부터 해야되지? 반복이었다.

그렇게 결국 이때까지 블로깅한 내용을 둘러봤다.
그리고 내가 할 일을 단계로 정리했다.

  1. 유효성 검사는 백엔드에서도 하니까 살짝 후순위
  2. 로그인이 된다. 확인되면 로컬 storage에 회원정보 저장 ← 콘솔로 확인
  3. ACCESS 토큰 전달

추후에는 바뀌지만 당장은 이렇게였다.


🤯 머리가 그냥 없어져버린 오후

밥도 먹었겠다. 패기롭게 의자에 앉았다.
나의 뇌는 패기롭게 다시 초기화가 되었다.

하얀색을 좋아한다지만, 머릿속이 그렇게 되기를 바라진 않았다.
시간은 계속 가기만 하고, 초조해지기만 했다.

좋다. 이제 다시 순서를 세워보자.

  1. 유효성 검사: 사용자가 올바른 형식의 정보를 제출하는지 확인할 수 있습니다.
  2. 로그인 로직 구현: handleLogin 함수 내에서 유효성 검사가 통과한 경우에 실제 로그인 로직을 추가하세요. 이 단계에서는 서버로 사용자의 이메일과 비밀번호를 전송하여 실제 인증을 수행합니다. 서버에서는 입력된 정보를 확인하고, 유효한 사용자인 경우 인증 토큰을 발행하여 클라이언트에게 전달합니다.
  3. 인증 토큰 관리: 로그인이 성공하면 서버에서 발급한 인증 토큰을 클라이언트에서 관리해야 합니다. 이 토큰은 로그인된 사용자의 인증 상태를 유지하는 데 사용됩니다. 일반적으로는 브라우저의 쿠키나 로컬 스토리지에 토큰을 저장합니다.
  4. 로그인 상태 관리: 인증 토큰을 기반으로 사용자의 로그인 상태를 관리해야 합니다. 로그인 상태에 따라 UI를 다르게 표시하거나 보호된 페이지에 액세스하는 권한을 부여할 수 있습니다. 이를 위해 React의 상태 관리 라이브러리나 Context API를 활용할 수 있습니다.
  5. 로그아웃 구현: 사용자가 로그아웃할 수 있도록 로그아웃 버튼이나 메뉴를 구현합니다. 로그아웃을 클릭하면 클라이언트의 인증 토큰을 삭제하고 로그아웃 상태로 변경합니다.
  6. 보안 강화: 보안 측면을 강화하고, 로그인 시도 횟수 제한, 비밀번호 재설정 절차, HTTPS 사용 등을 고려하여 보안을 강화합니다.
  7. 서버 측 구현: 클라이언트 로그인 구현과 별도로 서버 측에서도 로그인 처리 및 보안을 강화해야 합니다. 서버에서는 입력된 정보를 검증하고, 비밀번호 해싱 및 사용자 인증 절차를 구현해야 합니다.
  8. 사용자 경험 개선: 사용자가 로그인을 원활하게 할 수 있도록 경험을 개선합니다. 에러 메시지를 명확하게 제공하거나, 로딩 상태를 표시하는 등의 기능을 추가할 수 있습니다.

나의 유일한 친구 Chat-GPT가 말해줬다.

당장 해야되는 순서로 요약하면 유효성 검사 ➡️ 로그인 로직 구현이었다.

좋다. 바로 들어가자. 근데 어떻게 코드를 짜야하지?

그렇게 오후의 시간은 또 가기 시작했다.


🤯 흰머리가 자라나는 느즈막한 오후

이제 나는 로그인 페이지 코드를 조금은 끄적이고 있었다.

계획도 있었다. 그런데 아차.. 회원가입이 되어야 로그인을 하지 않는가..?
!^@#%#!!%#%!%#% 정말 안풀리는 하루였다.

괜찮다. 회원가입 페이지로 옮기자. 계획도 있었다.

  1. 회원가입: 클라이언트 → 엔드포인트로 서버에 이메일 패스워드 유저네임 →
    서버는 유효성 검사 돌리고 중복 여부 검사 →
    클라이언트 (에러가 나면 중복된게 있는거임, 성공하면 로그인 페이지로 리다이랙트 처리)
  2. 로그인: 클라이언트 → 서버 엔드포인트로 이메일 & 패스워드 확인

그리고 현재 API 사양서를 참고하면 로그인 실패 시, 에러 처리가 하나밖에 없다.
이것을 백엔드에게 두 개로 분기 요청할 것을 스터디 장에게 조언받았다.

유저가 로그인 실패한 경우라면, 첫 째로 이메일이 DB에 존재하지 않는 경우이다.
즉 아예 가입한 적이 없다는 뜻이다. 두 번째로는 이메일이 존재는 하나 비번이 틀린 경우이다.

그래서 두 개로 분기하는 것이 좋다는 것이다.

바로 부팀장님에게 달려가서 요청드렸지만, 살짝 힘들 것 같다고 하셨다.
그 이유를 요약해서 말하자면, 보안의 단계가 하락하는 것이었다.
굳이 힘들게 보안 단계를 낮출 수 없다는 말은 설득적이었다.

그래서 지금은 이렇게 메세지를 조금 더 구체적으로 바꿔주셨다 👍

그리고 JWT 토큰을 쓰며, 이것은 팀원 중 한명의 요청으로 body에 담아서 전달할 것이라 하셨다.

이것을 기반으로 코드를 짜기 시작했다.


로그인 & 회원가입 페이지 유효성 검사

이런식으로 회원가입, 로그인 차례로 완성이 되었다.
둘이 코드는 거의 비슷했으나 회원가입은 Name 부분이 하나 더 있다는거?

특히 오늘 알게 된 개념이 있는데, 바로 정규 표현식이라는 개념이다.
문자열 패턴을 표현하는데 사용되는 특별한 형식 언어(Formal Language)라고 한다.

  const nickNameRegex = /^[가-힣a-zA-Z]{2,6}$/;
  const emailRegex = /^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$/;
  const passwordRegex =
    /^(?=.*[A-Za-z])(?=.*\\d)(?=.*[$@$!%#?&])[A-Za-z\\d$@$!%#?&]{8,16}$/;

굉장히 특이하게 생겼다.. 이 블로그가 굉장히 도움이 되었다 👍👍

// 회원가입 페이지 유효성 검사
  
...
  
  const nickNameRegex = /^[가-힣a-zA-Z]{2,6}$/;
  const emailRegex = /^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$/;
  const passwordRegex =
    /^(?=.*[A-Za-z])(?=.*\\d)(?=.*[$@$!%#?&])[A-Za-z\\d$@$!%#?&]{8,16}$/;

  const handleLogin = (e) => {
    e.preventDefault();

    const newErrors = {};

    if (!nickName) {
      newErrors.nickName = 'Nickname cannot be empty.';
    } else if (!nickNameRegex.test(nickName)) {
      newErrors.nickName = 'The nickname is not valid.';
    }

    if (!email) {
      newErrors.email = 'Email cannot be empty.';
    } else if (!emailRegex.test(email)) {
      newErrors.email = 'The email is not a valid email address.';
    }

    if (!password) {
      newErrors.password = 'Password cannot be empty.';
    } else if (!passwordRegex.test(password)) {
      newErrors.password = 'No user found with matching email';
    }

    setErrors(newErrors);

    if (Object.keys(newErrors).length === 0) {
      // Perform login logic
    }
  };

...


위 코드는 사양서를 따라서 제작해서 크게 어렵지는 않게 끝낼 수 있었다.

로그인 & 회원가입 로직

로직은 도대체 어떻게 짜야되는건가? 라는 의문은 막상 작성하니 김이 식었다.
아주 쉽게 생각하면 'CRUD' 였다. 감이 어지간히 안잡혔었던 모양이다.

// 회원가입 페이지 로직 - Post

...
// 유효성 검사 코드
...

  if (Object.keys(newErrors).length === 0) {
      try {
        const response = await axios.post('{열어준 서버 도메인}/users/signup', {
          // Input value
          nickName,
          email,
          password,
        });

        if (response.status === 201) {
          console.log('Sign up successful');
          // 회원가입 성공 시 필요한 동작 수행
        } else {
          console.error('Sign up failed');
          // 회원가입 실패 시 필요한 동작 수행
        }
      } catch (error) {
        console.error('An error occurred:', error);
      }
    }
...


일단 시간은 이미 오후 11시이다. 시간이 너무 지체되었다.
초간단하게 로직을 작성하고 백엔드와 통신해보기로 하였다.

클라이언트 유효성 검사가 살짝 말썽을 부린 것 제외하고는 정말 간단하게 성공해버렸다.

🎉🎉 첫 통신의 순간이었다 🎉🎉

서버에도 정상적으로 데이터가 들어왔다고 사진을 보내줬다.
백엔드와 프론트엔드 모두 기뻐하고 있었다 ㅎ.ㅎ

중복된 이메일로 가입하려하면, 콘솔창에서 error 내용을 찾을 수 있었다.
경로는 Error ➡️ response ➡️ data ➡️ message 이었다.

사양서에 기입되어 있는 것처럼 똑같이 "사용중인 이메일 입니다."라고 표기되었다.

이어서 로그인도 시도해보았다.

새로운 아이디로 가입 후, 로그인을 시도하니 성공할 수 있었다.

console.log(response)로 콘솔창에서 반응 내용을 확인할 수 있었다.

그 중에서 토큰이 data에 담겨있는 것을 확인할 수 있었다.

따라서 console.log(response.data.token)으로 바로 확인이 가능했다.
이 값을 쿠키에 담는 것이 아니라, body에 담기로 했으므로 이에 따른 추가 작업이 필요할 것이다.

추가적으로 mypage에 대한 정보를 가져오는 get을 요청하도록 시도하였다.

근데 여기서 아주 치명적인 문제가 생겨버린다.

🌹 전설의 진정 빨간맛 CORS 그 녀석을 만나버린다.

// 회원가입 페이지 로직 - Get

...
// Post
...

  const mypageResponse = await axios.get(
    '{열어준 서버 도메인}/users/mypage',{},
            {
              headers: {
                Authorization: response.data.token, // 토큰을 Bearer 스타일로 헤더에 포함
                'Access-Control-Allow-Origin': `http://localhost:3000`,
                'Access-Control-Allow-Credentials': 'true',
                'Content-Type': 'application / json',
                'ngrok-skip-browser-warning': '69420',
              },
              withCredentials: true,
            },
          );

아니.. 네트워크 창에선 200 정상으로 뜨는데 왜그러냐고!!!!!!!!!!!!

알고보니.. axios.get(url[, config]) 틀에서 ,{},가 잘못 들어갔다.
config는 두 번째 인자로 설정 객체를 전달할 수 있는데 중괄호 {}가 두 번째 인자로 전달된 것이었다.

const mypageResponse = await axios.get(
  '{열어준 서버 도메인}/users/mypage',
  {
    headers: {
      Authorization: response.data.token,
      'Access-Control-Allow-Origin': 'http://localhost:3000',
      'Access-Control-Allow-Credentials': 'true',
      'Content-Type': 'application/json',
      'ngrok-skip-browser-warning': '69420',
    },
    withCredentials: true,
  }
);


삭제시켜주니 정상적으로 돌아갔다.

네트워크창에서도 정상적으로 200 !!

그동안 고생하신 부팀장님의 흔적..


☕️ 새벽 6시에 마무리하는 오늘

글까지 정리하다보니 벌써 6시네.. 내일 어떡하지..
뭐 아무튼 다른 팀원분께서 questions 페이지도 만들어주셨고, 이제 슬슬 끝을 향해 가고 있다.

이것은 내가 백엔드에게 드렸던 질문인데 남겨두고 싶다.

  1. 로그인시 토큰을 쿠키에 저장하고 싶어요.
    httponly 설정을 통해서 쿠키를 설정해주실 수 있나요?

    → Jwt 토큰을 쓰며, 토큰을 쿠키가 아니라 바디에 담아서 주겠다.

  2. 인가를 어떤식으로 구현하실 예정이신가요?
    저는 유저를 클라이언트에서 갖고있다가 새로고침 되면 해당 정보로 유저를 채워넣고
    이후에 요청에서 토큰이 만료됐다는 에러가 오면 로그아웃 처리를 할 생각입니다.

내일은 2번에 해당하는 내용을 서로 조정할 예정이다.
추가로 회원가입 시, 각 상황에 맞는 에러처리도 따로 설정해줘야 한다.



🎤 11일차


오전

이제 Questions 페이지가 완성되고 보니 왼쪽에 Sidebar
들어갈 예정인데 굳이 홈 화면에서 드롭다운이 필요할까 싶었다.

처음에 제작할 때도 혹시 몰라서 test로 커밋하면서
올려놨었는데 이제 다 완성이 되었으니 의논할 타이밍이었다.

의논 끝에 없애기로 하였고, 대신 내가 홈 배너 부분을 클릭하면
Questions 페이지로 이동하게 하면 어떠냐는 의견이 채택되었다.

뭔가 내가 사용자라면, 처음에 사이드 바도 없고 어디로 어떻게
이동해야할지 모를 것 같다는 생각에서 출발하게 되어서 수정까지 마무리했다.

후에 Login, Register 로직에서 에러 처리를 막 커스텀 하기엔 시간이 턱없이
부족할 것 같아 response.data를 받아 message를 alert에 그대로 띄워줬다.


오후

오후에 구현한 타임라인은 다음과 같다.

  1. 로그인 회원가입 로직 PR 나머지 구현
  2. FE 브랜치 프사 추가
  3. LocalStorage에 토큰이 있으면 nav 변경
  4. 프사 누르면 마이페이지 이동

로그인 회원가입 로직 PR 나머지 구현

첫 번째로 서버에서 받아온 token을 클라이언트 localStorage에 저장하도록 수정해주었다.


🎉 팀 중간 발표

모두모두과찬..이십니다..

후에 오후 2시쯤인가? 팀 발표가 있었는데 최애의 i팀 발표가 있었다.
현재까지의 최애의 i팀 진행상황을 재미있게 브리핑하려고 노력했던 것 같다.

아무래도 애니메이션의 주제를 사알짝 가지고 있다보니까 거부감이 안들었으면 했다.
그리고 또한 진지할 때는 진지한 모습을 보여주고 싶었다.

칸반 보드부터 Github, 디스코드 모든 부분을 공개했다.

그 결과.. 다들 열심히 호응해주셔서.. 행복했습니다.. 전 행복한 찐따에요 🥹🥹


Nav 컴포넌트에 프로필 사진 추가

이미지 파일을 추가하고, 로직은 삼항연산자를 사용했다.

로그인 시, 프로필 사진 & 로그아웃 버튼 노출
로그아웃 시, 로그인 & 회원가입 버튼이 노출되도록 했다.

또한 추후에 추가될 mypage를 위해서 프로필 사진을 클릭하면 해당 페이지로 이동하게 했다.


localStorage에 토큰이 있으면 nav 변경

localStorage에 토큰이 있는지 확인하고 Nav를 변경하는 것은
처음에 Nav 컴포넌트 안에서 모두 구현하는줄 알았는데 알고보니 각각 달랐다.

각각 nav는 토큰 로직, login 페이지는 로그인 상태를 관리해야 했다.

둘은 각각 로그인 상태를 같이 공유한다.

여기서 이 공유하는 상태를 관리하는 방법은 크게 두 가지를 생각했다.

다음은 예시이다.

  1. Context API
  2. App.js

Context API

// 1. 새로운 Context 생성 (AuthContext.js)

import React, { createContext, useContext, useState, useEffect } from 'react';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  useEffect(() => {
    const token = localStorage.getItem('access_token');
    setIsLoggedIn(!!token);
  }, []);

  return (
    <AuthContext.Provider value={{ isLoggedIn, setIsLoggedIn }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);
// 2. AuthProvider를 최상위 컴포넌트로 감싸기 (App.js)

import React from 'react';
import ReactDOM from 'react-dom';
import { AuthProvider } from './AuthContext'; // 경로는 실제 파일 경로에 맞게 수정
import App from './App'; // 실제 App 컴포넌트 경로에 맞게 수정
import './index.css';

ReactDOM.render(
  <React.StrictMode>
    <AuthProvider>
      <App />
    </AuthProvider>
  </React.StrictMode>,
  document.getElementById('root')
);
// 3. Login 컴포넌트에서 useAuth를 사용하여 로그인 상태 업데이트 (Login.jsx)

import { useAuth } from './AuthContext';

// ...

const Login = () => {
  const { setIsLoggedIn } = useAuth();

  // ...

  const handleLogin = async (e) => {
    // ...

    if (Object.keys(newErrors).length === 0) {
      try {
        // ...

        setIsLoggedIn(true);

        // ...
      } catch (error) {
// ...
// 4. Nav 컴포넌트에서 useAuth를 사용하여 로그인 상태를 동기화 (Nav.jsx)

import { useAuth } from './AuthContext';

// ...

const Nav = () => {
  const { isLoggedIn, setIsLoggedIn } = useAuth();

  // ...

  const handleLogout = () => {
    // ...

    setIsLoggedIn(false);

    // ...
  };

  // ...
};

App.js

// 1. App 컴포넌트에서 로그인 상태를 관리 (App.js)

import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Nav from './Nav'; 
import Login from './Login';
import './App.css';

function App() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  useEffect(() => {
    const token = localStorage.getItem('access_token');
    setIsLoggedIn(!!token);
  }, []);

  const handleLogin = () => {
    setIsLoggedIn(true);
  };

  const handleLogout = () => {
    localStorage.removeItem('access_token');
    setIsLoggedIn(false);
  };

  return (
    <Router>
      <Nav isLoggedIn={isLoggedIn} handleLogout={handleLogout} />
      <Routes>
        <Route path="/users/login" element={<Login handleLogin={handleLogin} />} />
        {/* ... */}
      </Routes>
    </Router>
  );
}

export default App;
// 2. Nav 컴포넌트에서 isLoggedIn 상태와 로그아웃 함수를 전달 (Nav.jsx)

import { Link } from 'react-router-dom';

function Nav({ isLoggedIn, handleLogout }) {
  // ...

  return (
    // ...
      <UserSection>
        {isLoggedIn ? (
          <UserSection>
            <ProfileSection onClick={() => navigate('/users')}>
              <img src={profile} alt="Profile" />
            </ProfileSection>
            <LogoutSection onClick={handleLogout}>Log out</LogoutSection>
          </UserSection>
        ) : (
          <UserSection>
            <LoginSection as={Link} to="/users/login">
              Log in
            </LoginSection>
            <RegisterSection as={Link} to="/users/register">
              Sign up
            </RegisterSection>
          </UserSection>
        )}
      </UserSection>
    // ...
  );
}

export default Nav;
// 3. Login 컴포넌트에서 로그인 성공 시 handleLogin 함수를 호출 (Login.jsx)

function Login({ handleLogin }) {
  // ...

  const handleLogin = async (e) => {
    // ...

    if (Object.keys(newErrors).length === 0) {
      try {
        // ...

        handleLogin(); // 로그인 성공 시 상태 업데이트

        // ...
      } catch (error) {
        // ...
      }
    }
  };

  // ...
}

export default Login;


이 방법 중 2번(App.js)에서 전역 상태 관리를 택했다.
전 프로젝트에서 다뤄봤던 경험도 있고, 추후에 1번으로 옮겨갈 생각도 있었다.

그렇게 부모-자식 관계를 이용하여 간단하게 상태를 공유해주었다.

여기까지 2주차 마무리하겠다!



🎤 2주차_Weekend (최애의 아이 2기..?)


이번주 메인은 '서버와의 통신'이었다.

일단 통신에 막힘 없도록 힘 써준 우리 부팀장님과
부족한 팀장을 서포트해준 우리 팀원들에게 너무 고맙다 👍

거의 사실 배워가면서 코드를 구현하고 있다.

처음에 UI 제외하고 통신으로 들어갈 땐, 아예 순서도 몰랐으니까 말이다.

특히 첫 통신 순간과 발표 시간은 이번 주에 하이라이트 꿀잼이었다 🍯🐝

아 그리고 조금의 납치(?)를 당했다. 주말에 디스코드 방이 갑자기 하나 생겼고,
Main 프로젝트도 같이 하자는 팀원들의 말이 있어서 곧 최애의 i팀 2기가 예정되었다.

부족한 나를 다시 참가시켜준다니 영광일뿐이다 !
만약 재참가하게 된다면, 이번을 경험삼아 더욱 도약해야겠다.

글을 다듬고 있는 지금은 월요일인데, 모든 기능이 거의 끝났다고 봐야한다.
마지막으로, 살짝의 스포인 홈페이지 체험을 경험해보자.

profile
아이디어가 넘치는 프론트엔드를 꿈꿉니다 🔥

0개의 댓글