기존 작업을 하다가 리팩토링을 추가하였다.

기존의 코드는 https://asdf1326.tistory.com/21 전에 글로 올려논 부분이고 이부분을 리팩토링을 진행하였다.

 

작업 계기는 현 회사에서 불편한 api 호출 방식으로 사용 중인 부분이 있어서 이부분을 개선 작업에 들어갔다.

 

현회사에서는 src 에 바로 axios 폴더가 들어있었고 그안에 post get put 등등 들어있었다.

현재 가장 많이 사용하는 post get 에대한 부분만 따로 리팩토링을 진행하였다.

 

기존 코드

import axios from 'axios';
import * as process from 'process';

export const axiosClient = axios.create({
  headers: {
    'Content-type': 'application/json; charset=UTF-8',
    accept: 'application/json,',
  },
});

axiosClient.interceptors.request.use(
  (config) => ({
    ...config, withCredentials : true,
  }),
  (error) => Promise.reject(error)
);

axiosClient.interceptors.response.use(
  async (response) => {
    return response;
  },
  (error) => {
    const errorMessage = error.response.data;
    throw errorMessage;
  }
);
export type AxiosMethod = 'get' | 'put' | 'post' | 'delete' | 'patch';

//T: tax-account-console C: company
export type prefixUrl = 'T' | 'C' | 'CT' | 'HOS' | 'U' | 'local' | 'N';

export async function callAxios(
  url: string,
  method: AxiosMethod,
  prefixUrl: prefixUrl,
  data?: any,
  customContentType?: string
) {
  let resData: any;

  //url이 있을경우 데이터 요청한다.
  if (url !== undefined && url.length > 0) {
    //prefix를 붙인다.
    let apiUrl = `${process.env.NEXT_PUBLIC_CLIENT_PROXY_URL}`;

    switch (prefixUrl) {
      case 'T':
        apiUrl += process.env.NEXT_PUBLIC_TAX_ACCOUNT_URL + url;
        break;

      case 'C':
        apiUrl += process.env.NEXT_PUBLIC_COMPANY_URL + url;
        break;

      case 'CT':
        apiUrl += process.env.NEXT_PUBLIC_CONTENTS_URL + url;
        break;

      case 'HOS':
        apiUrl += process.env.NEXT_PUBLIC_HOS_URL + url;
        break;

      case 'U':
        apiUrl += process.env.NEXT_PUBLIC_USER_ACCOUNT_URL + url;
        break;

      case 'N':
        apiUrl += process.env.NEXT_PUBLIC_CLIENT_NOTIFICATION + url;
        break;
      case 'local':
        apiUrl += 'http://4954-61-74-228-131.ngrok-free.app/' + url;
        break;

    }

    switch (method) {
      case 'get':
        resData = await axiosClient.get(apiUrl, {
          params: data,
        });
        break;

      case 'post':
        resData = await axiosClient.post(apiUrl, data, {
          headers: {
            'Content-Type': customContentType || 'application/json; charset=UTF-8',
          },
        });
        break;


      case 'put':
        resData = await axiosClient.put(apiUrl, data);
        break;

      case 'patch':
        resData = await axiosClient.patch(apiUrl, data);
        break;

      case 'delete':
        resData = await axiosClient.delete(apiUrl, {
          data,
        });
        break;

      default:
        break;
    }
  }

  return resData.data;
}

//header에서 다운로드 파일 이름
const downloadFileName = (res: any) => {
  const disposition = res.headers['content-disposition'];
  const fileName = decodeURI(
    disposition
      .match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)[1]
      .replace(/['"]/g, '')
  );
  return fileName;
};

export type ExcelParamType = {
  url: string;
  prefixUrl?: prefixUrl;
  data?: any;
};

export interface ExcelParamSetterProps {
  setExcelParam: ({ url, data, prefixUrl }: ExcelParamType) => void;
}

//파일 다운로드
export function callFileDownloadAxios(
  url: string,
  prefixUrl?: prefixUrl,
  data?: any
) {
  if (url !== undefined && url.length > 0) {
    //prefix를 붙인다.
    let apiUrl = `${process.env.NEXT_PUBLIC_CLIENT_PROXY_URL}`;

    switch (prefixUrl) {
      case 'C':
        apiUrl += process.env.NEXT_PUBLIC_COMPANY_URL + url;
        break;

      case 'CT':
        apiUrl += process.env.NEXT_PUBLIC_CONTENTS_URL + url;
        break;

      case 'HOS':
        apiUrl += process.env.NEXT_PUBLIC_HOS_URL + url;
        break;

      case 'U':
        apiUrl += process.env.NEXT_PUBLIC_USER_ACCOUNT_URL + url;
        break;
    }

    axiosClient
      .get(apiUrl, {
        responseType: 'blob',
        params: data,
      })
      .then((res) => {
        const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
        const link = document.createElement('a');
        link.href = downloadUrl;
        link.download = downloadFileName(res);
        document.body.appendChild(link);
        link.click();
        link.remove();
      });
  }
}

//파일 업로드
export async function callFileUploadAxios(url: string, data: any) {
  const response = await axiosClient.post(url, data, {
    headers: {
      'Content-Type': 'multipart/form-data',
    },
  });

  return response.data;
}

 

 

기존 코드는 위에같이 적혀있었다. 각 res,req 에대한 타입지정은 없었으며 다른 페이지에서도 지저분하게 적히며 전혀 관리가 안되는 부분들이 많이 차지하였다. 

 

위에 코드처럼 중구 난방으로 관리가 덜된 느낌이 강한 코드들이 퍼져있었다.

처음에 와서 스타트업 특성상 빠른 업무가 우선시 되며 최적화된 코드보다는 기능만 구현하기 위하여작업된 코드와... 신입이 만든.. 구조덕에 난해한 코드가 작성이 되어있었다....정말 고생 많이 했다...

 

이를 계속 쓰다보면 정리되어있지 않는 상태로 계속 작성이 되면 추후에는 리팩토링이 아니라 리뉴얼 정도의 작업이 되는걸 막기 위하여 타입을 정의 하며 어떻게든 백엔드와 인터페이스 정의를 같이 하여 맞추어나가려고 노력했다....

(같이 일한 백엔드 개발자의 노고가 정말 컷다..ㅠ) 

 

일단 가장 정리하기 쉬운 공지사항 부분먼저 차차 잡아나가려고 해당 구조를 잡았다.

common 에 넣은 이유는 공통적으로 관리에 앞서 구분 하기 위함으로 구조를 만들었다. 

 

api 서버단에서 dto 요청을 미리 클라이언트가 요청할 수 있는 상황을 대비하여 다음과 같이 구조를 짯다.

실제 서비스 엔드포인트에 이름을 따와서 정리를 해주었고 dto 파일을 작성한다.

 

 

인터페이스를 지정해주고 응답값을 받을 부분을 적어준다.

 

인터페이스 구현

 

 

함수 생성 (토큰 관련 작업이 추후에 바뀔 가능성이 있다고 하셔서 그부분을 예비로 작성하였다.)

더많은 작업이 필요하지만 대충 리프레쉬 토큰을 가지고 다시 액세스 토큰을 발급 받는 과정을 적을 수있게 미리 공간을 만들어둔다.

 

useQuery 도 추가적으로 관리 할 수 있도록 추가해준다.

 

이제 모든 인터페이스 정의는 끝났다 실제로 사용할 수 있게 다른페이지에서 호출 해본다.

 

 

notice.ts 는 실제 호출 가능한 함수를 담는 공간으로 작성한다.

 

이부분은 api 엔드포인트들을 관리하는 공간이다.

위와 같이 만들어준다.

 

useQuery 를 사용하여 작성한 코드

 

useQuery 가 필요없을 경우 일반적인 코드 작성 방법이다.

 

전체 코드는 토이프로젝트로 따로 생성 해두었다.

https://github.com/asdf132645/submoudleReact

 

GitHub - asdf132645/submoudleReact

Contribute to asdf132645/submoudleReact development by creating an account on GitHub.

github.com

 

 

실무에서 성능 고려하여 유지보수등 가독성을 끌어올리기위하여 아래와 같은 작업을 진행했으며 부분적으로 올려서 예시 추가된 코드

 

리팩토링한 코드(타입 지정) - api 호출 타입 재정의 서비스단으로 api dto 생성 하는 부분 추가 -> https://github.com/asdf132645/reactToyProject/ 

 

GitHub - asdf132645/reactToyProject: reactToyProject

reactToyProject. Contribute to asdf132645/reactToyProject development by creating an account on GitHub.

github.com

 

 

현회사에서 무한으로 스크롤 페이징이 되는 처리를 요구하였다. 

여기에 맞게 코드를 작성하고 만들기전에 전에 겪었던 문제점을 생각했다.

 

전에는 스크롤 위치를 잡아 구현 했을때 제대로된 동작을 안하고 엉뚱하게 흘러가는 부분이 있어서 다른 방법을 모색 하였다.

 

https://developer.mozilla.org/ko/docs/Web/API/IntersectionObserver

 

IntersectionObserver - Web API | MDN

Intersection Observer API의 IntersectionObserver 인터페이스는 대상 요소와 상위 요소, 또는 대상 요소와 최상위 문서의 뷰포트가 서로 교차하는 영역이 달라지는 경우 이를 비동기적으로 감지할 수 있는

developer.mozilla.org

 

new IntersectionObserver 라는 것을 사용하기로 하였다.

 

 

api 를 호출 하는 부분을 생성하고 

 

 <div className="app-container op-profile">
                <div className="open-main-mypage">
                    <div className="order-List" style={{minHeight: '930px'}}>
                        {inquiries?.length === 0 ?
                            <>
                                <div className="no-data">
                                    <div className="ic"><img src="/images/app/ico-no-pay-data.svg" alt=""/></div>
                                    <p className="description">
                                        내 문의내역이 없습니다.
                                    </p>
                                </div>
                            </>
                            :
                            <>
                                <ul>
                                    {inquiries.map((inquiry: any, idx: number) => (
                                        <li
                                            key={idx}
                                            onClick={() => {
                                                router.push(`/myPage/inquiry/inquiryDetail?id=${inquiry.id}&status=${inquiry.status}`);
                                            }}
                                        >
                                            <div>
                                                <p
                                                    className={
                                                        inquiry.categoryName === 'ETC' ?
                                                            'category col-4'
                                                            : inquiry.categoryName === 'JOIN' ?
                                                                'category col-3'
                                                                : inquiry.categoryName === 'TAX' ?
                                                                    'category col-1'
                                                                    : inquiry.categoryName === 'EMPLOYEE' ?
                                                                        'category col-2'
                                                                        : 'category col-4'
                                                    }
                                                >
                                                    {inquiry.category}
                                                </p>
                                                <p className="subject"
                                                   style={{whiteSpace: 'pre-wrap'}}>{inquiry.title}</p>
                                                <p className="datetime">{formatDate(inquiry.writtenAt)}</p>
                                            </div>
                                            <p className={`status ${inquiry.status === 'A' ? 'end' : 'ing'}`}>
                                                {inquiry.status === 'A' ? '답변완료' : '답변대기'}
                                            </p>
                                        </li>
                                    ))}
                                </ul>
                            </>
                        }
                        <div ref={loadMoreRef} style={{height: '10px'}}></div>
                    </div>
                </div>
            </div>

 

태그 부분을 생성해주고

 

<div ref={loadMoreRef} style={{height: '10px'}}></div>

 

원하는 위치에 ref 로 해당 영역을 잡아준다. 해당 위치로 스크롤이 내려갈 시 api 호출하는 방법이다.

 

 

간단하게 useEffect 훅을 사용하여 작성하면 기능 구현 완료

 

실무에서 겪은 문제중에 조금 난처한 상황이 발생했다. vuejs 를 쓸때는 use state 같은 기능이 따로 없었다. 

setter 함수 부분으로 업데이트를 쳐줄떄 state 값이 가끔 비동기적으로 움직이 있어 확인 해본 결과 usestate 는 비동기처리방식이었다. 

 

외적으로 자바스크립트는 싱글 스레드 = 싱글 콜 스택 = 한번에 한번씩 동작을 한다. 

느린 작업등 콜 스택에 오래도록 남아있는 부분을 없애주고 해결 해주기 위해 있는것이 비동기 처리이며 이러한 작업을 없애기 위해서 비동기 작업이 존재한다. 

이런 동작 원리로 인해 시점 문제들이 발생 한다. useState 도 같은 맥락에 문제점이 있었다.

세터함수 부분은 리액트에서 리렌더링을 일으키기 때문에 주의해서 사용해야하며

 

useCallback, useMemo 등 훅을 사용시에는 연속 호출로 인해 부득이하게 예상과 다른 값이 반영이 되는 부분이 있다.

배치처리를 통해 하나의 리렌더링으로 묶어서 사용하여야 예상과 같은 방식으로 동작을 한다.

 

시점에대한 문제는 리액트에서 제공하는 useEffect(동기방식) 를 사용하여 시점을 잡아 줄 수 있다.

 

vue 와 다르게 리액트에서는 라이프사이클은 useEffect 를 주로 사용하다.

 

 

 

현 회사에서 React 프레임워크를 사용하고 있다. 

스타트업 특성 상 정리가 안되어있고.. es7에 장점을 최대한 이끌어내지 못하는 코드들이 많이 보였다..

 

프리랜서들이 걸쳐 나간 흔적이 꽤나 짙어보였다.

타입스크립트를 사용 하는 이유가 사라질정도로 any 도 남발하였고 api 문서가 정리된부분이 없어서 꽤나 애를 많이 먹는 작업이였다.

 

store 를 사용하지 않고 세션스토리지를 사용하는 모습이 많이 눈에 보였다.

리액트 store 는 익숙치 않아서 일단 책에서 읽은 내용과 검색한 내용을 합쳐서  zustand 라이브러리를 사용하여 store를 만들어 봤다.

 

기본 구조는  

type InitialState = ReturnType<typeof getDefaultInitialState>;
type StoreAction = {
    // 다양한 액션들의 타입을 정의
    // 예를 들어, setHospitalName, setIsHospital, setUserId 등 다양한 액션들이 정의되어 있음
};

const getDefaultInitialState = () => {
    // 초기 상태를 정의하는 함수
};

export const useStore = create(
    // persist 미들웨어를 사용하여 Zustand 상태를 생성
    persist<InitialState & StoreAction>(
        // set 함수를 통해 상태를 업데이트할 수 있는 액션들을 정의
        (set) => ({
            // 여러 액션들과 초기 상태를 포함한 객체를 반환
        }),
        {
            // persist 옵션 설정
            name: 'account',  // 상태를 지속시킬 때 사용할 이름
            storage: createJSONStorage(() => sessionStorage),  // 지속시킬 데이터를 저장할 storage를 설정 (여기서는 sessionStorage 사용)
        }
    )
)

 

위와 같은 형태로 만들어주고 사용 하였다. vuejs 에서 내장으로 있는 store와 거의 비슷한 느낌에 형태였다.

 

모든 프론트 프레임워크는 상태관리는 개념은 거의 비슷 하다고 느꼇다. 그부분에서 많은 어려움을 느끼지는 못하였다.

 

사용 방법은

 

type StoreAction = {
setCompanyName: (companyName: string) => void;

 

const getDefaultInitialState = () => ({
companyName: '',

 

const useStore = create(
persist<InitialState & StoreAction>(
setCompanyName: (companyName: string) => set({companyName}, false),

 

으로 값을 넣어주고 

 

import {useStore} from '@/lib/store';
const { companyName } = useStore();

 

위에처럼 변수로 선언해주고 사용한다.

'react > next' 카테고리의 다른 글

next 프로젝트 공통 서브모듈 구현  (0) 2023.11.14

구조

서브모듈 컴포넌트 - console-component

(콘솔 공통 컴포넌트는 컴포넌트 라이브러리 소스코드와 샘플을 실행 할 수 있는 storybook 으로 구성)

+- root
+- src
+- button
+- checkbox
+- multiplecheckbox # checkbox 테스트 페이지
+- dateboxpage # 날짜 상자 테스트 페이지
+- emptystate # 404 등 빈 페이지
+- imperative # experimental => 제거해도 무방
+- input
+- popup
+- querybox # 다중 필터 선택 상자
+- rolebox # 커스텀 체크 박스
+- serachbox # 검색 상자
+- selectbox # 동적/정적으로 바뀌는 드롭다운
+- selectformpage # experimental => 제거해도 무방
+- tabletest # experimental => 제거해도 무방
+- table # ag-grid기반 테이블 커스텀 컴포넌트
+- textarea
+- togglebtn
+- menutab # 라우터를 관리하는 레이아웃 공통 컴포넌트

공통 라이브러리를 분리 하여 재사용 할 수 있도록 구현 프로젝트 컴포넌트를 개발하는 목적이 아니면 이프로젝트를 단독으로 사용하는 경우는 없으며 웹프론트엔드 프로젝트의 gitsubmodule 로 포함 하여 사용

 

실제 프로젝트 컴포넌트 - mobile-web 

실제 프로젝트 연동 컴포넌트 생성

 

1. 프로젝트 클론

> cd mobile-app
> git submodule init component
> git submodule update ( clone)

 

2. yarn 패키지 설치

> yarn init
> yarn workspace component install

 

3. 프로젝트 개발 환경 및 빌드

// dev mode
yarn console:dev

// build mode
yarn console:build

 

모바일 웹 구성도

 

이것으로 서브모듈 연동 완료

 

테이블이라는 공통 항목등 

context API 주입성으로 만들어서 부모에서 자식객체를 가져다가 쓰는 방식으로 구현 등 여러가지를 구현가능하다 아래와 같이 예시를 만들어서 사용

 

테이블은 ag그리드를 사용하였고

import { ColDef, ColGroupDef } from 'ag-grid-community';

export type TableColumnProps =
| (ColDef | ColGroupDef) & { order?: number; field: string };

export type ITableCxtProps = {
column: TableColumnProps[];
addColumnProps: (props: TableColumnProps) => void;
};

 

실제 모바일 웹에서 사용 할때 테이블 모습

 

table.tsx 부분 코드

 

 

이런식으로 셋팅하여 퍼블리셔든 누구든 간결하게 옵션만 추가하면 돌아가는 방식으로 코드 구축을 한다.

공통 앱일경우 여러가지 방법이 있지만 

 

createContext 를 사용하여 하는 방법을 채택한건 vue slot 처럼 간결한 기능을 구현할 방법을 모색하다가 가장 비슷무리한 방법을 채택 하지만 위에 내가 작성한 방법은 전역으로 상태를 관리하는 방법이고 그에따른 단점이 있다.

 

공식 문서에만 봐도 컨텍스트 api 의 존재 이유는 명확하게 나와있다. prop 으로 단계별로 내리는 방법이 아닌 깊이 여부와 무관하게 데이터가 필요한 컴포넌트에서만 불러다가 사용이 가능하다. 이에따른 무분별한 사용은 현재 어디서 데이터가 내려오는지를 파악하기 힘들어지는 부분과 유지보수에 힘든 상황이 발생 하지 않도록 구현을하고 만들어야한다. 지금과 같은 공통 컴포넌트로 뺴놓은 상태에서는 컨텍스트 api 를 사용하는게 차선책으로 보여 선택하였다.

 

부모컴포넌트가 리렌더링이 되면 하위에 모든 컴포넌트도 리렌더링이 된다. 이런경우를 막기 위해서는 react memo 로 감싸면 되는데 하지만 이런식으로 구성하면 reactmemo 때문에 프로비더에 효과가 사라진다. props 를 가지고 변경하고 재렌더링하기 때문에 이런점을 주의해서 구현해야한다. 하위에 모든 컴포넌트를 리렌더링하는 프로비더를 사용할때는 주의를 요한다.

 

 

 

'react > next' 카테고리의 다른 글

React 프레임워크 + zustand 라이브러리 사용  (2) 2023.11.21

오늘은 리액트 를 통해 디비에 값을 뿌리는 작업을 진행 하겟당!

 

전에 작업한 링크

https://asdf1326.tistory.com/7

 

리액트 express 를 사용하여 MySQL 연동하기

오늘은 리액트 node js express 를 사용하여 MySQL 을 연동할것이다. 근데 오늘 몸상태가 최악..ㅠ 1. 프로젝트 생성 나는 vscode와 웹스톰을 번갈아가면서 쓴다 ㅎ 이번에는 vscode 로 진행을 하였다.~ 터

asdf1326.tistory.com

 

일단 필요한 구조를 설명하자면 

클라이언트 폴더, 서버 폴더 분리해서 직접 생성 한 디비를 연결작업을 진행해보겠다~!

클라이언트 생성명령어 npx create-react-app 폴더명칭 --template typescript

서버측은 위에 링크 참고

위에 폴더를 차례대로 생성 후 일단 디비를 생성

 

스키마 하나 새로 생성 후 use nodejs 로 데이터베이스 선택

위에처럼 생성

데이터 추가 후 명령어로 테이블 확인

 

index.js 

const express = require("express");
const app = express();
const mysql = require("mysql");
const PORT = process.env.port || 3000;

const db = mysql.createPool({
  host: "127.0.0.1",
  user: "root",
  password: "0000",
  database: "nodejs",
});

 app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
  res.header("Access-Control-Allow-Headers", "x-access-token, Origin, X-Requested-With, Content-Type, Accept");
  next();
});

app.get("/list", (req, res) => {
  const sqlQuery = "SELECT *FROM BOARD;";
  db.query(sqlQuery, (err, result) => {
    res.send(result);
  });
});

app.listen(PORT, () => {
  console.log(`running on port ${PORT}`);
});

여기서 주의할점이 가끔 cors 에러가 뜨곤 한다 미리 사전의 방지를 하는것이 좋다!

header에 추가적인 조치를 취해준다. 응답 헤더부분에 추가적으로 넣으면 된다. 내가 추가한 값이 제대로 들어가있다~

원래는 다른 코드를 써서 조작을해야 안전하게 하나의 도메인만 받는게 좋다 * 로 설정해둔 오리진 부분은 사실 많이 쓰지 않는 방식이다.!

인터넷 검색으로 치면 많은 설명들이 나온다 꼭 개발이 아닌과정에서는 확인 후 사용해주자~

이제 서버측은 셋팅이 끝낫당.!

클라이언트쪽으로 넘어가서 만들어보자~

클라이언트 소스확인 가능한 깃허브 주소 : https://github.com/asdf132645/client

 

GitHub - asdf132645/client

Contribute to asdf132645/client development by creating an account on GitHub.

github.com

import { Component } from "react";
import Axios from "axios";
import Table from "react-bootstrap/Table";
import Button from "react-bootstrap/Button"; 

const Board = ({
  id,
  title,
  registerId,
  registerDate,
}: {
  id: number;
  title: string;
  registerId: string;
  registerDate: string;
}) => {
  return (
    <tr>
      <td>
        <input type="checkbox"></input>
      </td>
      <td>{id}</td>
      <td>{title}</td>
      <td>{registerId}</td>
      <td>{registerDate}</td>
    </tr>
  );
};

/**
 * BoardList class
 */
class BoardList extends Component {
  state = {
    boardList: [],
  };

  getList = () => {
    Axios.get("http://localhost:3000/list", {})
      .then((res) => {
        const { data } = res;
        this.setState({
          boardList: data,
        });
      })
      .catch((e) => {
        console.error(e);
      });
  };

  /**
   */
  componentDidMount() {
    this.getList();
  }

  /**
   * @return {Component} Component
   */
  render() {
    // eslint-disable-next-line
    const { boardList }: { boardList: any } = this.state;

    return (
      <div>
        <Table striped bordered hover>
          <thead>
            <tr>
              <th>선택</th>
              <th>번호</th>
              <th>제목</th>
              <th>작성자</th>
              <th>작성일</th>
            </tr>
          </thead>
          <tbody>
            {
              // eslint-disable-next-line
              boardList.map((v: any) => {
                return (
                  <Board
                    id={v.BOARD_ID}
                    title={v.BOARD_TITLE}
                    registerId={v.REGISTER_ID}
                    registerDate={v.REGISTER_DATE}
                  />
                );
              })
            }
          </tbody>
        </Table>
        <Button variant="info">글쓰기</Button>
        <Button variant="secondary">수정하기</Button>
        <Button variant="danger">삭제하기</Button>
      </div>
    );
  }
}

export default BoardList;

npm install axios 로 모듈 설치해주고

우리는 서버 포트는 3000이다. 명확하게 클라이언트 포트도 나눠주자!

요로코롬 start 할때 포트 8000 으로 이어주자~ 그래야 겹치지 않고 서로다른 포트에서 연결이 잘되니깐!

이제 마지막으로 화면을 확인하고 네트워크를 봐주자~

잘나온다 ! 네트워크도 확인 해보자!

 

응답값이 아주이쁘게 들어가있다.!! 뷰만 할때는 못느낀건데.. 컴포넌트 반복문 쓸때 좀 낮설다.. map 함수를 써서 반환 시키는게 참 낮설다... 뷰는 v-for 써주고 key 써주면 아주 이쁘게 잘들가는뎅.. 참 낮설어...! 암튼이게 문제가 아니고 다음에는 테이블내용 추가 수정등을 해볼것이다~ 그리고 리덕스도 차근차근 적용하고~ 라우터도 해볼것이다~ 이것으로 코인의 백엔드 조작 탐방 일기 끄읏~~~

다음 일기에서 계속~!~

+ Recent posts