Giter Site home page Giter Site logo

playground-react's Introduction

PLAYGROUND-FRONT

  • npm 모듈 설치 시 package.json 에 포함시키기
npm install <package_name> --save
  • 설치한 라이브러리 정리
// node.js와 브라우저를 위한 Promise 기반 HTTP 클라이언트
axios

// proxy 설정
http-proxy-middleware 

// 라우터 설정
react-router-dom 

// Material UI
@mui/material @emotion/react @emotion/styled
// scss설치
npm install node-sass
사용 방법은 css 사용 방법과 똑같다. 
CSS 방식
import './App.css'

scss방식
import './App.scss'

프로젝트 실행 순서

  • npm 모듈 설치 (package.json)
npm install
  • 프로젝트 시작
npm start

🤝 커밋 규칙

태그 이름 설명
Feat 새로운 기능을 추가하는 경우
Fix 버그를 고친 경우
Style 코드 포맷 변경, 세미 콜론 누락, 코드 수정이 없는 경우
Refactor 리팩토링 (코드 및 환경변수 설정 변경)
Comment 필요한 주석 추가 및 변경
Docs 문서를 수정한 경우
Rename 파일 혹은 폴더명을 수정하거나 옮기는 작업만인 경우
Remove 파일을 삭제하는 작업만 수행한 경우

태그: 내용 (#이슈번호)
예시 ) Feat: 내용추가추가추가 (#1) , Refactor: Config 리팩토링 (#4)

playground-react's People

Contributors

heeju23 avatar jaynamm avatar kkyhh avatar mandoo1229 avatar moneyless1989 avatar

Stargazers

 avatar  avatar

Watchers

 avatar

playground-react's Issues

🔑 JWT 토큰 localStorege에 저장

const signUpHandler = () => {
    if (userId.length === 0 || userPassword.length === 0) {
      alert('이메일과 비밀번호를 입력하세요.');
      return;
    } // 아무런 값을 입력하지 않았을 때 alert를 호출합니다.

    const data = {
      userid: userId,
      password: userPassword,
    };

    axios //입력한 데이터를 받음.
      .post('/api/member/login', data)
      .then((response) => {
        console.log(response.data);
        // const responseData = response.data;
        const jwtToken = response.headers['authorization'];
        const refreshToken = response.data['refresh-token'];
        console.log(axios.defaults);
        // const refreshToken = response.data['refresh-token'];
        if (jwtToken && refreshToken) {
          axios.defaults.headers.common['Authorization'] = jwtToken;
          localStorage.setItem('accessToken', jwtToken);
          localStorage.setItem('refresh-token', refreshToken);

          // axios.defaults.data.common['refresh-token'] = refreshToken;
          // localStorage.setItem('refreshToken', refreshToken);
        }
        alert('로그인 성공했습니다.');

        navigate('/home', {
          // state: responseData,
        });
      })
      .then((result) => {
        console.log(result);
      })
      .catch((error) => {
        console.log(error);
        alert('로그인에 실패했습니다.'); //axios값에 데이터가 일치 하지 않으면 함수를 호출함
      });
  };

로그인을 성공하였을 때 localStorage에 AccessToken과 RefreshToken을 저장을 합니다.
AccessToken은 header에 저장이 되고, RefreshToken은 body에 저장이 됩니다. 저장된 값을 꺼내 localStorege에 저장합니다.

🌟 Issue: 리액트 이슈 정리 (23-05-16)

✅ HTML 와 JSX

코드들을 가져오거나 수정할 때 HTML 문법과 JSX 문법이 조금씩의 차이가 있어서 그냥 쓸 수 없고 수정을 해줘야 한다

예를들어 CSS를 적용하려는 class를 가져올 땐 HTML 에선 로 가능하지만 JSX는 으로 사용한다

 <div class='bar'>
                <div class="logo">Play</div>
                <div class="menu">
                    <ul>
                    <li><Link to="/notice"><a>공지사항</a></Link></li>
                        <li><a href="#">PlayGround Q&A</a></li>
                        <li><a href="#">실시간 인기게시물</a></li>
                        <li><a href="#">사이드 프로젝트</a></li>
                        <li><a href="#">채용정보</a></li>

  • js 적용 X
 <div className='bar'>
                <div className="logo">Play</div>
                <div className="menu">
                    <ul>
                    <li><Link to="/notice"><a>공지사항</a></Link></li>
                        <li><a href="#">PlayGround Q&A</a></li>
                        <li><a href="#">실시간 인기게시물</a></li>
                        <li><a href="#">사이드 프로젝트</a></li>
                        <li><a href="#">채용정보</a></li>

  • js 적용 O

✅ 혹여나 .js에서 CSS가 적용이 안된다면 인지 확인을 해보자


✅ Info: 리액트 패키지 디렉터리 구조

리액트 패키지 구조에 대해서 생각을 해봐야할 것 같아서 찾아봤다.
패키지 구조를 어떻게 해야할지 찾아보고 어떻게 구분해야할지 정리해봤다.

components

재사용 가능한 컴포넌트들
컴포넌트는 매우 많아질 수 있기 때문에 이 폴더 내부에서 하위폴더로 추가로 분류하는 경우가 많다고 한다.

assets

이미지 혹은 폰트와 같은 파일들을 저장
이미지와 같은 파일들을 public에 직접 넣는 경우도 있는데 따로 assets 을 만드는 경우와의 차이는 컴파일 시에 필요한지 여부이다.
파비콘과 같이 index.html 내부에서 직접 사용하여 컴파일 단계에서 필요하지 않은 파일들은 public 에 넣어야 하고
반면, 컴포넌트 내부에서 사용하는 이미지 파일인 경우 이 assets 폴더에 위치시켜야 한다.

hooks (= hoc)

커스텀 훅이 위치하는 폴더

pages

react router 등을 이용하여 라우팅을 적용할 때 페이지 컴포넌트들

constants

공통적으로 사용되는 상수들을 정의한 파일들이 위치하는 폴더, 변경없이 고정적으로 사용되는 파일들

config

config 파일이 많지 않은 경우 보통 최상위에 위치하지만
여러개의 config 파일이 있을 경우 폴더로 분리하기도 한다.

styles

css 파일들이 포함되는 폴더입니다.

services (= api)

보통 api 와 관련된 모듈 파일이 위치하며 auth와 같이 인증과 관련된 파일이 포함되기도 한다.

utils

정규표현식 패턴이나 공통함수 등 공통으로 사용하는 유틸 파일들이 위치하는 폴더

contexts

context API 를 사용할 때 관련 파일들이 위치
상태관리를 위해 contextAPI 대신 redux를 사용 할 경우 폴더 이름을 store로 사용하기도 한다.

🔑 로그인 방식

Cookie / Session / Token 인증 방식 종류

  • 보통 서버가 클라이언트 인증을 확인하는 방식은 쿠키, 세션, 토큰 3가지 방식이 있다.

🍪 Cookie 인증

쿠키는 Key-Value 형식의 문자열 덩어리이다.
클라이언트가 어떠한 웹 사이트를 방문할 경우, 그 사이트가 사용하고 있는 서버를 통해 클라이언트의 브라우저에 설치되는 작은 기록정보 파일이다. 각 사용자마다의 브라우저에 정보를 저장하니 정보 식별이 가능하다.

🍪 Cookie 인증 방식

  • 브라우저(클라이언트)가 서버에 요청(접속)을 보낸다.
  • 서버는 클라이언트의 요청에 대한 응답을 작성할 때, 클라이언트 측에 저장하고 싶은 정보를 응답 헤더의 Set-Cookie에 담는다.
  • 이후 해당 클라이언트는 요청을 보낼 때마다, 매번 저장된 쿠키를 요청 헤더의 Cookie에 담아 보낸다.
  • 서버는 쿠키에 담긴 정보를 바탕으로 해당 요청의 클라이언트가 누군지 식별하거나 정보를 바탕으로 추천 광고를 띄우거나 한다.

🍪 Cookie 방식의 단점

  • 강장 큰 단점은은 보안에 취약하다. 요청시 쿠키의 값을 그대로 보내기 때문에 유출 및 조작 당할 위험이 존재한다.
  • 쿠키에는 용량제한이 있어 많은 정보를 담을 수 없다.
  • 웹 브라우저마다 쿠키에 대한 지원 형태가 다르기 때문에 브라우저간 공유가 불가능하다.
  • 쿠키의 사이즈가 커질수록 네트워크에 부하가 심해진다.

🔑 Session인증

이러한 보안적인 이슈 때문에, 세션은 비밀번호 등 클라이언트의 민감한 인증정보를 브라우저가 아닌 서버 측에 저장하고 관리한다. 서버의 메모리에 저장하기도 하고, 서버의 로컬 파일이나 데이터베이스에 저장하기도 한다. 민감한 정보는 클라이언트에 보내지 않고 서버에서 모두 관리한다.

🔑 Session 인증 방식

  • 유저가 웹사이트에서 로그인하면 세션이 서버 메모리(혹은 데이터베이스) 상에 저장된다. 이 때, 세션을 식별하기 위한 Session id를 저장한다.
  • 서버에서 브라우저에 쿠키가에다가 Session id를 저장하낟.
  • 쿠키에 정보가 담겨이 ㅆ기 때문에 브라우저는 해당 사이트에 대한 모든 Request에 Session id를 쿠키에 담아 전송한다.
  • 서버는 클라이언트가 보낸 Session id와 서버 메모리에 관리하고 있는 Session id를 비교하여 인증을 수행한다.

🔑 Session 객체는 어떤 형태로 이루어져 있을까?

  • 세션 객체는 Key에 해당하는 Session ID와 이에 대응하는 Value로 구성되어 있다. value에는 세션 생성 시간, 마지막 접근 시간 및 User가 저장한 속성 등이 Map 형태로 저장된다.

🔑 Session 방식의 단점

  • 쿠키를 포함한 요청이 외부에 노출되더라도 세션 ID 자체는 유의미한 개인정보를 담고 있지 않는다.
  • 그란 해커가 Session ID 자체를 탈취하여 클라이언트인척 위장할 수 있다는 한계가 존재한다.
  • 서버에서 Session 저장소를 사용하므로 요청이 많아지면 서버에 과부하가 걸린다.

🔒 Token 인증

토큰기반 인증시스템은 클라이언트가 서버에 접속을 하면 서버에서 해당 클라이언트에게 인증되었다는 의미로 '토큰'을 부여한다. 이 토큰은 유일하며 토큰을 발급받은 클라이언트는 또 다시 서버에 요청을 보낼 대 요청 헤더에 토큰을 심어서 보낸다. 그러면 서버에서는 클라이언트로부터 받은 토큰을 서버에서 제공한 토큰과의 일치 여부를 체크하여 인증과정을 처리하게 된다.
기존의 Session 인증은 서버가 파일이나 데이터베이스에 세션정보를 가지고 있어야 하고 이를 조회하는 과정이 필요하기 때문에 많은 오버헤드가 발생한다. 하지만 토큰은 세션과는 달리 서버가 아닌 클라이언트에 저장되기 때문에 메모리나 스토리지 등을 통해 세션을 관리했던 서버의 부담을 덜 수 있다. 토큰 자체에 데이터가 들어있기 때문에 클라이언트에서 받아 위조되었는지 판별만 하면 디기 때문이다.
토큰은 앱과 서버가 통신 및 인증할 때 가장 많이 사용된다. 왜나하면 웹에는 쿠키와 세션은 있지만 앱에는 없기 때문이다.

🔒 서버기반 vs 토큰 기반

서버기반 인증 시스템 (Session)

  • 서버의 세션을 사용해 사용자 인증을 하는 방법으로 서버측에서 사용자의 인증정보를 관리하는 것을 의미한다. 그러다보니, 클라이언트로부터 요청을 받으면 클라이언트의 상태를 게속해서 유지해놓고 사용한다. 이는 사용자의 증가함에 따라 성능의 문제를 일으킬 수 있으니 확장성이 어렵다는 단점을 지닌다.

토큰기반 인증 시스템 (Token)

  • 이러한 단점을 극복하기 위해 토큰기반 인증시스템이 나타났다.
  • 인증받은 사용자에게 토큰을 발급하고, 로그인이 필요한 작업일 경우 헤더에 토큰을 함께 보내 인증받은 사용자인지 확인한다.
  • 이는 서버기반 인증 시스템과 달리 상태를 유지하지 않으므로 Stateless한 특징을 가지고 있다.

🔒 Token 인증 방식

  • 사용자가 아이디와 비밀번호로 로그인을 한다.
  • 서버측에서 사용자(클라이언트)에게 유일한 토큰을 발급한다.
  • 클라이언트는 서버측에서 전달받은 토큰을 Cookie or localStroage에 저장해 두고, 서버에 요청을 할 때마다 해당 토큰을 HTTP 요청 헤더에 포함시켜 전달한다.
  • 서버는 전달받은 토큰을 검증하고 요청에 응답한다. 토큰에는 요청한 사람의 정보가 담겨있기에 서버는 DB를 조회하지 않고 누가 요청했는지 알 수 있다.

🔒 Token 방식의 단점

  • Cookie/Session과 다르게 토큰 자체의 데이터 길이가 길어, 인증 요청이 많아질 수록 네트워크 과부하가 심해질 수 있다.
  • Payload 자체는 암호화되지 않기 때문에 유저의 중요한 정보는 담을 수 없다.
  • 토큰을 탈취당하면 대체하기 어렵다. (따라서 사용 기간 제한을 설정하는 식으로 극복한다. )

✅ 새로고침을 했을 때 localStorege에 저장되어 있던 토큰이 사라짐.

로그인을 성공한 후 페이지를 이동하고 난 뒤 새로고침을 시도하면 localStorege에 저장되어 있던 accessToken과 refreshToken이 사라지는 현상이 나타났다. 그 이유를 살펴보니

const action = () => {
  localStorage.removeItem('accessToken');
  localStorage.removeItem('refreshToken');
};

로그아웃 기능에서 오류가 있었다. 그래서 위에 코드 처럼 수정을 하니 새로고침을 진행해도 localStorege에 저장되어 있던 Token이 사라지지 않고 그대로 유지 되었습니다. 이후 로그아웃 버튼을 눌러야 localStorege에 저장되어 있던 accessToken과 RefreshToken이 삭제됩니다. 그로 인하여 오류를 해결하였습니다.

🔒 JWT (JSON Web Token)이란?

우선 우리는 JWT 토큰 방식으로 관리를 하기 위해 JWT토큰 방식에 대해 알아야 한다.

🔑 JWT

  • JWT(JSON Web Token)란 인증에 필요한 정보들을 암호화시킨 JSON 토큰을 의미한다. 그리고 JWT기반 인증은 JWT 토큰(Access Token)을 HTTP헤더에 실어 서버가 클라이언트를 식별하는 방식이다.
  • JWT JSON 데이터를 Base64 URL -safe Encode를 통해 인코딩하여 직렬화한 것이며, 토큰 내부에는 위변조 방지를 위해 개인크를 통한 전자서명도 들어있다. 따라서 사용자가 JWT를 서버로 전송하면 서버는 서명을 검증하는 과정을 거치게 되며 검증이 완료되면 요청한 응답을 돌려준다.
  • Base64 URL -safe Encode는 일반적인 Base64 Encode에서 URL에서 오류없이 사용하도록 +, /를 각각 -, _ 로 표현한 것이다.

🔑JWT 구조

JWT는 .을 구분자로 나누어지는 세 가지 문자열의 조합이다.
.을 기준으로 좌측부터 Header, Payload, Signature을 의미한다.


스크린샷 2023-07-03 오후 12 23 03


Header에는 JWT에서 사용할 타입과 해시 알고리즘의 종류가 담겨 있으며, Payload는 서버에서 첨부한 사용자 권한 정보와 데이터가 담겨있다. Signature에는 Header, Payload를 Base64 URL -safe Encode를 한 이후 Header에 명시된 해시함수를 적용하고, 개인키(Private Key)로 서명한 전자서명이 담겨 있다.

🔑 JWT 인증과정

  • 사용자가 ID, PW를 입력하여 서버에 로그인 인증을 요청한다.
  • 서버에서 클라이언트로부터 인증요청을 받으면, Header,. PayLoad, Signature를 정의한다.
  • Header, PayLoad, Signature를 각각 Base64로 한 번 더 암호화하여 JWT를 생성하고 이를 쿠키에 담아 클라이언트에게 발급한다.
  • 클라이언트는 서버로부터 받은 JWT를 localStorage 저장한다. (쿠키나 다른 곳에도 저장 가능 보통 localStorage에 저장을 한다. )
  • API를 서버에 요청할 때 Authorization header에 AccessToken을 담아서 보낸다.
  • 서버가 할 일은 클라이너트가 Header에 담아서 보낸 JWT가 내 서버에서 발행한 토큰인지 여부를 확인하여 일차한다면 인증을 통과시켜주고 아니라면 통과시키지 않으면 된다. 인증이 통과되었으면 페이로드에 들어있는 유저의 정보들을 select해서 클라이언트에게 돌려준다.
  • 클라이언트가 서버에 요청을 했는데, 만일 AccessToken의 시간이 만료되면 클라이언트는 RefreshToken을 이용해서 서버로부터 새로운 엑세스 토큰을 발급받는다.

🌟 Issue: 리액트 이슈 정리 (23-05-12)

✅ 게시글 Sort 를 사용해 작성일자 최신순으로 정렬

  • 기존 코드
   @GetMapping(value = "/getallfeeds")
    public Map<String, List<FeedDto>> feedMain() {
        Map<String, List<FeedDto>> feeds = new HashMap<>();
        feeds.put("feeds", feedService.feedPage());
        return feeds;
    }

기존의 코드는 게시글 전체를 불러오는 기능이다
작성하게 되면 최신글이 위로 올라오지 않고 아래로 쌓여가게 되어 있어서 정렬을 하게 만들어준다
처음에는 서버와 클라이언트 둘 다 코드를 추가해서 적용이 되는줄 알았다


➡️ 서버 스프링부트

 @GetMapping(value = "/getallfeeds")
    public Map<String, List<FeedDto>> feedMain() {
        Map<String, List<FeedDto>> feeds = new HashMap<>();
        List<FeedDto> sortedFeeds = feedService.feedPage();

        // Sort the feeds by creation date in descending order
        sortedFeeds.sort(Comparator.comparing(FeedDto::getCreationDate).reversed());

        feeds.put("feeds", sortedFeeds);
        return feeds;
    }
  • Java util 중 Comparator와 Date 기능을 사용

➡️ 클라이언트 리액트

useEffect(() => {
        axios({
            method: 'GET',
            url: "/api/feed/getallfeeds",
            headers: {
                'Content-Type': 'application/json'
            }
        })
            .then((res) => {
                console.log(res.data);

                let feedData = res.data.feeds;
                feedData.sort((a, b) => b.timestamp - a.timestamp);

                setFeeds(feedData);
            })
            .catch((err) => {
                console.log(err);
            })
    }, []);
  • React Home.js 컴포넌트안의 axios에 feedData.sort((a, b) => b.timestamp - a.timestamp); 코드를 추가

이 방식에 맞춰 실행해보니 오류는 나지 않았지만 Home화면에 피드가 사라졌었다 원인을 모르겠어서 강사님께 질문을 해보니 서버측에서만 간단하게(?) 수정하는 방법이 있었다

☀︎서버 스프링부트 코드 수정

➡️ FeedService.java

public List<FeedDto> feedByPage(Pageable pageable) {
        List<Feed> feedList =  feedRepository.findAllBy(pageable); // 추후 페이징 처리(검색 갯수 제한) 필요
        List<FeedDto> feedDtoList = feedList.stream().map(FeedDto::new).toList();
        return feedDtoList;
    }
  • FeedService에 feedByPage 메소드 추가

➡️ FeedAPIController.java

@GetMapping(value="/getallfeeds")
    public Map<String, List<FeedDto>> feedMain(
            @RequestParam(defaultValue = "0") int start,
            @RequestParam(defaultValue = "10") int size
    ) {
        Pageable pageable = PageRequest.of(start, size, Sort.by(Sort.Direction.DESC, "uploadTime"));
        Map<String, List<FeedDto>> feeds = new HashMap<>();
        feeds.put("feeds", feedService.feedByPage(pageable));
        return feeds;
    }
  • @RequestParam start 와 size 를 추가
  • Pageable 을 만들어서 PageRequest로 start size값을 주고 Sort로 정렬
  • Sort.Direction으로 AESC 와 DESC 중 선택
  • “uploadTime”은 작성일자가 있는 DB
  • feedByPage 에 pageable을 불러온다(?)

이 수정으로 게시글을 작성할때 최신순으로 정렬하는걸 구현
추후에 start와 size로 화면에 보여질 게시물 갯수를 조절
start 1 에는 1-10 까지의 게시글이 2에는 11부터 20까지의 게시글이 들어가는것 같다

화면에 게시글 전체가 보이면 안되니 갯수를 정하고 하단에 내려가면서 불러오는 기능으로 연결해야겠다

✅ Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.

QnA 게시판에서 하나의 게시글의 제목을 클릭해서 들어가면 다음과 같이 에러가 발생한다.

image



동작하는 과정은 아래와 같다.

Qna.js

제목을 클릭하면 onClick() 이벤트 함수가 실행된다.

<table>
    <th>번호</th>
    <th>제목</th>
    <th>작성자</th>
    <th>생성일</th>
    {questions.map((question) => (    
        <tr>
            <td>{question.id}</td>
            <td onClick={() => {onClickQnaViewHandler(question.id)}}>{question.title}</td>
            <td>{question.author}</td>
            <td>{question.createdDate}</td>
        </tr>
    ))}
</table>
  • onClick() 함수
    questionId 값을 가지고 /qna/view 로 이동한다.
const onClickQnaViewHandler = (questionId) => {
    navigate(`/qna/view/${questionId}`, {
        state: questionId
    });
}

QnaView.js

questionId 를 가져와서 API 를 이용해 해당 질문 글을 가져온다.

const location = useLocation();
const getId = location.state;

const [questionId, setQuestionId] = useState("");
const [questionViewData, setQuestionViewData] = useState([]);

setQuestionId(getId);
console.log(questionId);

axios({
    method: "GET",
    url: `/api/qna/question/view/${questionId}`
})
.then((res) => {
    console.log(res.data.question);

    // setQuestionViewData(res.data.question);
})
.catch((err) => {
    console.log(err);
})

일단 동작 과정은 이런데 다시 한번 처음부터 생각해봐야겠다.

🌟 Issue: 리액트 이슈 정리 (23-05-09)

오늘의 이슈

부트스트랩

  1. react .js 프로젝트에서 부트스트랩을 이용할 때 Include via CDN에서 link 와 script를 public 폴더안의 index.html 안에 넣어주어야 한다
  2. link 태그는 단순 CSS 작용
  3. script 태그는 동작 CSS 작용

modal 사용 과정

  1. button 클릭시 modal 을 띄우는 창 구현시도
  2. card 형태 안에 button modal 을 넣으려고 시도 했으나 에러 발생
  3. 원인 - 기존 card의 클래스 코드들은 필요가 없었다
  4. 해결 - button 클래스 안에 modal 코드를 넣어주기만 하면 작동한다

🌟컴포넌트 라우트 사용시
중첩으로 인한 .css 오류

  1. 각 팀원별 프로젝트 작업후 main 으로 합치면서 컴포넌트 라우트 기능을 사용
const Header = () => {
    return (
        <div>
            <Nav /> // Header.js 에 Nav.js 를 불러옴
        <div/>
    );
}
  1. 이 방식으로 리액트 안에서 간편하게 코드를 구성
  2. 컴포넌트가 여러개가 생겼을 때 import .css 가 적용 안되는 페이지들 발견
  3. 원인 - 계속해서 라우트를 하는 과정 중 return 문 안의
    들이 중첩되어 경로가 꼬이게 된다
  4. 해결 - react에서 기본적으로 쓰이는
    대신 <> 프래그먼트를 사용해보니
    가 벗겨져서 css import가 작동
const Header = () => {
    return (
        <>
            <Nav /> // <> 프래그먼트로 변경
        </>
    );
}
  1. css 파일들에 id 값과 class 들을 각각 설정해주어 구분해야할 필요가 있어보임
  2. Redux 를 사용해서 컴포넌트들을 관리할 방법을 찾아볼 필요가 있어보임

✅ AccessToken으로 API호출하기

  useEffect(() => {
    axios({
      method: 'GET',
      url: '/api/mypage',
      headers: {
        'Content-Type': 'application/json',
        Authorization: localStorage.getItem('accessToken'),
      },
    })
      .then((response) => {
        let myPageFeedDtoList = response.data;
        setLists(myPageFeedDtoList);

        setMypage(response.data.memberDto);
        setFollowMyPage(response.data.followMyPageDto);
        setMyWriting(response.data.myPageFeedDtoList);
        // setMyPageFeedDtoList(response.data.myPageFeedDtoList);
        setMyPageFeedDtoList.filter(response.data.myPageFeedDtoList);
      })
      .catch((error) => {
        console.log(error);
      });
  }, []);

API를 요청할 때 AccessToken이 일치하면 API를 호출합니다. AccessToken을 headers에 담아서 호출 요청을 합니다.

🔑 axios interceptor 활용하기

axios interceptor란?

axios interceptor는 비동기 통신이 이루어지기 전에 요청(request)나 응답(response)을 가로채 작업을 추가로 수행한 뒤 통신이 이루어지게 하는 기능이다.
axios interceptor는 로그인 시 토큰 인증할 때만 사용하지는 않지만 토큰 인증시 요청을 보낼 때 header에 accessToken을 함께 실어 보내고, 응답 받을 때 에러가 발생하면 (accessToken이 만료)되면 refreshToken을 확인하여 재발급을 요청한다.

import axios from 'axios';

axios.interceptors.request.use(
  function (request) {
    request.headers['accessToken'] = localStorage.getItem('accessToken');
    return request;
  },
  function (error) {
    return Promise.reject(error);
  }
);

axios.interceptors.response.use(
  function (response) {
    return response;
  },
  async (error) => {
    const [response, config] = error;
    if (response.status === 401) {
      const { data } = await axios.get('/api', {
        baseURL: 'http://localhost:3000',
        params: {
          //refresh Token으로 재발급 요청 401에러가 뜨면?
          token: localStorage.getItem('refreshToken'),
        },
      });
      const { accessToken } = data;
      //액세스 토큰을 localStorege에 저장
      localStorage.setItem('accessToken', accessToken);
      // 새 액세스 토큰을 header에 저장
      config.headers['accessToken'] = accessToken;
      // 재요청
      return await axios(config);
    }
    return Promise.reject(error);
  }
);
export default axios;

🤪 Issue: React Kakao 로그인 이슈 발생!

React에서 Kakao 로그인 이슈 발생!

const kakaoApiKey = "ef7c392eab30902039c2e6e68c1fd356"   
  const redirect_uri = "http://localhost:3000/signin"
  const kakaoURL = `
  https://kauth.kakao.com/oauth/authorize?client_id=${"ef7c392eab30902039c2e6e68c1fd356"}&
  redirect_uri=${"http://localhost:3000/signin"}&response_type=code`

kakao에서 api키를 받고, 그 키 값으로 uri를 설정하여 로그인 기능을 만들었으나API를 받아오지 못했다.
좀 더 공부해서 API값을 받아오는 방법을 알아봐야 할거 같습니다.

이 방법을 토대로 구글 아이디로도 로그인을 할 수 있도록 구현하도록 하겠습니다.

🌟 Issue: 리액트 이슈 정리 (23-05-10)

✅ CSS 중복 확인

  • 기존에 작업하던 프로젝트들의 컴포넌트들을 분리 했을 때 CSS가 중첩되서 적용이 안되는 문제 발견
  • 적용 안되는 부분에 개발자도구로 확인후 ::before 라는 태그를 확인
  • 처음에는 ::before가 어떤 작용인지 알 수가 없어서 중복되는곳이 있나 단순하게 css들을 주석처리 했다가 취소했다가를 반복하며 하나씩 확인
  • 하나씩 뜯어본 결과 변화가 없자 CSS의 중복문제가 아닌것으로 가정하고 처음에 발견했던 ::before 태그를 다시 확인
  • PlayGround의 logo 를 css 로 적용할 때 ::after 를 사용했던게 기억이나서 적용이 안되던 태그에 ::before를 넣고 CSS 를 수정하여 다시 적용
div.icon ::before {
    color: #7d2192;
}

홈화면과 메인화면 둘 다 잘 적용된것을 확인했다
🌟앞으로는 단순반복 작업을 하기전에 HTML들의 각 태그명을 우선적으로 확인하는것이 좋을것 같다

🌟 Issue: 리액트 이슈 정리 (23-05-11)

✅ z -index 설정

FeddModal 게시글 쓰기 박스를 구현후 화면 이동시 모달창이 웹 상단 헤더 메뉴바 위로 스크롤 이동하는 문제점 발견

스크린샷 2023-05-11 오후 6 51 28

css 로 z-index 설정을 주어서 피드화면과 같이 뒤쪽으로 자연스럽게 이동하게 수정했다

스크린샷 2023-05-11 오후 6 50 18
  • Modal.css
.card {
    max-width: 665px;
    margin: 0 auto;
    padding: 10px;
    margin-top: 30px;
    z-index:0; // 0 값을 부여
}
  • header.css
header {
    position: sticky;
    top: 0;
    align-items: center;
    background-color: white;
    padding: 10px;
    box-shadow: 0px 2px 5px #888888;
    z-index: 1; // 헤더에 1 값 card에 0값이 부여되어 스크롤 이동시 card 가 자연스럽게 뒤로 이동함
}

✅ Info: .js vs .jsx 차이

리액트 코드를 작성할 때 파일의 확장자를 .js 또는 .jsx 를 통해 작성할 수 있는데
두 파일에 대한 차이에 대해서 확인해봐야할 것 같다.

JSX 에 대한 간단한 소개

우선 JSX 파일에 대한 설명은 다음과 같다.
JSX는 JavaScript XML의 약자로 자바스크립트에서 XML을 추가한 확장형 문법이다.
큰 특징은 자바스크립트에서 HTML 문법을 사용할 수 있다는 점이고 주로 리액트나 다른 프론트엔드 프레임워크에서도 사용이 가능하다고 한다.

예를 들어,

const element = <h1>Hello, world!</h1>

자세한 내용에 대해서는 더 찾아봐야 알 것 같고 결국에는 어떤 확장자를 사용하면 좋을지 팀원들과 얘기를 해봐야할 것 같다.

✅ Issue: 자바스크립트 onClick() 이벤트 함수 이슈

아래와 같이 게시판에서 타이틀을 클릭하면 해당 글로 들어가게 onClick() 이벤트를 사용했다.

변경 전

{questions.map((question) => (    
    <tr>
        <td>{question.id}</td>
        <td onClick={onClickQnaViewHandler(question.id)}>{question.title}</td>
        <td>{question.author}</td>
        <td>{question.createdDate}</td>
    </tr>
))}

하지만 클릭했을 때 들어가는 것이 아니라 QnA 게시판에 들어가자마자 onClick 이벤트가 실행되면서 글 세부 내용으로 페이지가 바로 이동해버렸다.

해당 내용에 대해서 찾아보니 onClick() 이벤트에서는 화살표 함수를 사용하지 않으면 함수가 바로 실행된다고 한다.

공식 문서에 자세한 이벤트 사용법에 대해서 나와있다.

리액트 공식 문서 - 이벤트 처리하기

그래서 다음과 같이 코드를 수정해서 정상적으로 동작하는 것을 확인했다.

변경 후

{questions.map((question) => (    
    <tr>
        <td>{question.id}</td>
        <td onClick={() => {onClickQnaViewHandler(question.id)}}>{question.title}</td>
        <td>{question.author}</td>
        <td>{question.createdDate}</td>
    </tr>
))}

바로 이벤트가 실행되지 않도록 화살표 함수 을 사용해서 클릭했을 때 함수가 실행되도록 만들었다.

✅ React 아이디 찾기 이슈!

const idCheckHandler = () => {

        const email = {
            "email": userEmail,
        };

        axios
        .post("/api/member/search/id", email)
        .then((response) => {
            console.log(response.data);
            const responseData = response.data;
            alert("아이디를 찾았습니다.")
        })

        .then((result) => {
            console.log(result);
        })

        .catch((error) => {
            console.log(error)
            alert("입력하신 이메일로 가입한 아이디가 없습니다.")
        })
    }

아이디 찾기 버튼을 눌렀을 때 console에 찍히지 않고 웹에 바로 출력을 하고 싶었으나 웹에 출력이 되지 않았습니다.
그래서 생각을 해보다 useState를 활용하면 결과 값을 출력할 수 있을 거라 생각하여 responseData를 useState에 담아 출력하였습니다.

const [searchResult, setSearchResult] = useState('');
/...생략.../
axios
        .post("/api/member/search/id", email)
        .then((response) => {
            console.log(response.data);
            const responseData = response.data;
            setSearchResult(responseData)
            alert("아이디를 찾았습니다.")

중간에 set에 담아 출력해준 출력해주었더니 아이디 찾기 결과 값이 웹이 출력되었습니다.
스크린샷 2023-05-17 오후 11 12 12

DB안에 있는 Email을 통해 아이디를 찾는 방식으로 코드를 작성해보았습니다.
위와 같이 Email을 입력하였을 때 '1234'라는 아이디를 찾을 수 있었습니다.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.