본문 바로가기
부트캠프/Codestates

2주 프로젝트 백서

by z쿳쿳z 2020. 8. 23.
728x90
반응형

▶Project 명 : Puri

▶프로젝트 의도 : 아는 수학 문제를 계산 실수(숫자 오기입 등)로 틀린 경우 어디서 틀렸는지 확인해 볼 수 있는 서비스

▶팀원: 4명

  -front : 2명(내가 맡은 일 : react 기능 구현 / 다른 페어는 CSS 담당)

  -back: 2명

 

▶kick off

  - 함수를 어디까지 구현이 가능하도록 할지 설정필요 => 우선 1차 함수 도전

  - user 마다 글씨체를 인식 및 저장해서 사진 적용 => 각 user마다 글씨체 적용이 어렵다고 판단 기존 사진 하나로 우선 적용

 

▶구현영상

▶내가 한일

1. React - Router

  - 코드 선정 : SPA 형태로 웹을 구현 하는 것이 아니기 때문에 React - Router를 사용 했다.

  - 코드 기능

     1. Route 기능 

     2. history 기능

 

  - 코드 예시

import React from 'react';
import { Switch, Route } from 'react-router-dom';

<Switch>
  <Route path="/" component={Index} exact />
  <Route path="/main" component={Main} />
  <Route path="/result" component={Result} />
  <Route path="/reviewnote" component={ReviewNote} />
  <Route path="/reviewdetail" component={ReviewDetail} />
</Switch>

 history.push({
   pathname: '/result',
   state:{user:location.state}
   });
 };
  
  

  - 코드리뷰 : React - router를 사용하면 server의 구축이 있어야 하고 거기서 route를 정해 주는 줄 알았다. 하지만 검색을 통해서 server의 구축없이 path를 통해서 충분히 router를 이용할 수 있었다.

페이지에서 페이지로 이동할 때, component 들이 부모와 자식 관계를 가지고 있지 않아서 데이터를 넘겨 줄 수 없었다. history를 통해서 넘겨 줄 수 있지 않을까? 라는 생각을 했고, react-router 사이트에서 확인 결과 객체 형태로 넘겨주는 것을 알게 되었다. database에서 데이터를 가져오려면 authorization  값으로 구글아이디를 사용했기 때문에, OAuth로 로그인 했을 때, db에서 데이터를 가져올 때 이용하였다. 

 

2. Functional - Hook 기능 구현

  - 코드 선정 : 같이 front를 담당했던 페어가 요즘 대세가 Hook을 사용한다고 했다. 이전에 react를 공부하면서 class 대해서만 공부 했는데, 이번에 공부하면서 사용하면 좋을 같아서 functional - hook을 사용하기로 했다.

 

  - 코드 기능

     1. 버튼기능(onClick > 태그추가, form data 전송, 태그내용 출력, 페이지 이동)

     2. input 버튼(onChange > 글자수제한, count)

     3. get 데이터 받아오기(useEffect, useState) / post 데이터 전송

 

  - 코드 예시

  const [uploadImg, setUpload] = useState('');

  const handlebutton = () => {
    setValue('Solution');
  };
  
const [notesData, getNotesData] = useState('');
useEffect(() => {
  axios
    .get('http://localhost:3004/notes', {
      headers: {
        Authorization: userId,
      },
    })
    .then((res) => {
      getNotesData(res.data);
    });
}, []);

  - 코드리뷰 : class보다 hook을 사용했더니 조금 더 명시적(?)으로 코드를 작성할 수 있어서 보기 용이했다. javascript에서 자주 보던 함수 형태가 더욱 익숙해서 그런것 같다.

class에서 사용하는 setState 대신 hook에서는 useState를 사용해서 state 값을 바꿔야 한다.

 

  - 아쉬운 점 : useEffect()가 class에서 사용하는 lifecycle을 대신해준다고 해서 사용을 했다. 하지만, 아직 두번째 인자로 사용하는 배열은 미숙하다. 빈 배열이 오면 일단 한번만 실행되는 정도로 알고 있다. 빈 배열이 아니라 element를 가지고 있을 때, 사용법을 익혀야 한다.

useEffect()이외 다른 기능들도 있었는데 추가적으로 공부가 필요하다.

 

3. local storage 사용

  - 코드선정 : user가 저장버튼을 클릭 하지 않으면 bucket 저장하지 않도록 해서 bucket을 효율적으로 관리를 위해 사용

 

  - 코드기능

     1. localstorage 파일 저장

     2. localstorage 파일 호출

 

  - 코드 예시

//localstorage store
  const uploadFile = async (e) => {
    let reader = new FileReader();
    let file = e.target.files[0];
    reader.onloadend = await function () {
      setImg(reader.result);
      localStorage.setItem('upload', JSON.stringify(reader.result));
    };
    reader.readAsDataURL(file);
  };

//localstorage download
  const [img, getImage] = useState('');
  const [picUrl, getPicUrl] = useState('');
  
  useEffect(() => {
    getImage(JSON.parse(localStorage.getItem('upload')));
    axios.get('http://localhost:3004/upload').then((res) => {
      getPicUrl(res.data.location);
    });
  }, []);
  
  

  - 코드리뷰 : local에 저장하는 방식은 처음 도전 했던터라, 구현하는데 시간이 걸렸다. 특히 FileReader 객체로 파일 값을 읽으면 base64형태로 저장되었기 때문에 콘솔로 확인 했을 때, 많이 당황을 했다. 구글링을 통해서 base64를 img 태그 src에 입력하면 이미지를 불러 올 수 있었다. 그래서 local에 있는 이미지를 저장하고 바로 불러와 사용할 수 있었다.

local 저장하면서 key 값으로 'upload'로 설정해서 바로 올렸던 파일을 쉽게 가져오기 위해 설정했다.

 

  - 아쉬운 점 : 시간이 너무 많이 사용되었고, 3시간안에 못해서 같은 팀원들에게 도움을 요청을 했다. 팀원 중에 한번 사용한 팀원이 있어서 팀원이 작성한 코드를 참고 했다. 구글링이 미숙했다.

 

4. Server - S3

  - 코드선정 : 업로드한 이미지 파일을 database에 저장을 하려고 검색을 했으나,  database를 조금 더 효율적으로 관리하기 위해서 S3에 이미지를 저장하고, database에는 url만 저장하는게 관리하기 용이하다고 해서 일단 S3에 저장 하기로 했다.(아직 정확한 이유는 잘 모름)

S3에서 저장할 수 있도록 도와주는 모듈 multer와 multer-s3를 사용했다. multer 없이 사용이 가능하지만,  없이 사용하면 필요한 데이터를 명시적으로 선언을 하고 key값을 가지기 위해 filename같은 값을 따로 할당을 해야 하기 때문에 express에서 제공해주는 multer (form data를 다룰 수 있는)를 사용했다.

form data는 이미지를 ajax로 전송할 때 사용하는데, form data를 없이 사용하면 buffer, base64, 이진수 같은 값이 전송 된다. 하지만 객체 형태로 보는게 조금 더 편하기 때문에 form data를 사용한다. 그리고 form data 사용하는 가장 큰 이유는 페이지 전환없이 전송이 가능하다. form 태그에 action을 이용하면 페이지가 전환(?)이라고 해야하나 새롭게 렌더링 된다.

 

  - 코드기능

     1. S3 사진 저장

     2. S3 사진 출력

     3. S3 사진 삭제

 

  - 코드 예시

//client
const data = new FormData();
data.append('file', file);
await axios
  .post(url, data, {
    headers: {
      'Content-Type': 'multipart/form-data',
    },
  })
  .then((res) => getKey(res.data.key));

//server
const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");
let s3 = new aws.S3({
  region: "ap-northeast-2",
  accessKeyId: process.env.AWS_ACCESS_KEY,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
});
const path = require("path");

//add
let upload = multer({
  storage: multerS3({
    s3: s3,
    bucket: "purireviewnote",
    key: function(req, file, cb) {
      //username + date.now()
      let extension = path.extname(file.originalname);
      cb(null, Date.now().toString() + extension);
    },
    acl: "public-read-write"
  })
});

let location = "";
let imgFile;
app.post("/upload", upload.single("file"), function(req, res, next) {
  imgFile = req.file;
  location = imgFile.location;
  res.json(imgFile);
});

//down load
app.get("/upload", function(req, res, next) {
  s3.listObjects(
    {
      Bucket: "purireviewnote"
    },
    (err, data) => {
      if (err) {
        return console.log(err);
      }
      data.location = location;
      res.json(data);
    }
  );
});

//delete
app.post("/delete", function(req, res, next) {
  s3.deleteObject(
    {
      Bucket: "purireviewnote",
      Key: imgFile.key
    },
    (err, data) => {
      if (err) {
        return console.log(err);
      }
      res.end();
    }
  );
});

  - 코드리뷰 : back-end에서 mathpix에 url을 전달 받으려면 S3에 이미지를 저장하고 url을 받아와야 한다고 해서 S3 저장을 할 수 있도록 코드를 작성했다.(이전에 로컬에서 받아오는 것은 그대로 살려두고, S3에도 이미지를 저장했다.)

코드를 작성하면서 파일을 올렸던 data를 조금더 쉽게 가져오기위해 location과 imgFile을 post/get Callback 함수가 아니라 외부에 설정을 했다. location은 업로드한 이미지 파일의 url을 바로 가져올 수 있었다. 업로드한 이미지의 location은 따로 저장을 했다가 get 요청이 오면 url을 바로 보내 주도록 했다.

s3 메소드를 활용해서 이미지를 지우려고 했다. 지우려고 했더니 필수요소가 bucket 이름과 이미지 key 값이었다. 그래서 외부변수에 따로 저장해서 이미지 삭제를 위해 사용했다. 간단한 버튼을 만들고 삭제기능을 넣어서 bucket에서 삭제가 잘되는 것을 확인 했으나, 실제로 삭제기능은 사용하지 않았다. 나중에 오답노트에서 S3에 저장한 이미지를 사용하기 위해서...

 

  - 아쉬운 점 : solution 버튼을 누르지 않고 나갔을 때, 이미지가 삭제되도록 했으면, 불필요한 사진을 저장하는 것을 막을 수 있지 않았을까 한다.

 

5. client - server 연결

  - 코드선정 : axios를 사용해서 server와 연결 하였다. fetch를 사용할 수도 있었지만,  fetch는 body 데이터를 받아오고 axios는 배열객체타입 형태로 받아오기 때문에 따로 stringify를 할 필요 없어서 조금 더 유용한거 같아 axios를 사용했다.

 

 - 코드기능

     1. get요청

     2. post 요청

 

  - 코드 예시

const handleSaveNote = (e) => {
  e.preventDefault();
  const noteData = {};
  noteData.picUrl = picUrl;
  noteData.resultText = 'resultText';
  noteData.comment = comment;
  noteData.review = false;
  noteData.tags = tags;
  axios.post('http://localhost:3004/note', noteData, {
    headers:{
      authorization: userInfo
    }
  })
  history.push({
    pathname: '/reviewnotes',
    state: { user: userInfo },
  });
};

  - 코드리뷰 : 이제것 받아온 데이터와 정보를 종합해서 버튼을 클릭했을 때, database에 post 요청을 보내는 코드를 작성했다. 

 

 

-- 전체리뷰 --

◎ React에서 Hook 기능을 다 활용을 하지 못해, 못내 아쉬웠다. 공부가 조금 부족했던거 같았다.

  ★ 결론 : 추가 공부가 필요하다.

 

◎ front-end를 담당했고, sever 구축은 back-end에서 담당하기로 했는데, back-end에서 Mathpix 활용에 어려움이 있어서 S3 기능을 직접 server 작성했다. 어쩌다보니 front - back(full stack)을 하게 되었다.

 

◎ 의사소통이 잘 안되었다 → 서로 하고 있는 것을 바로바로 파악이 안되었고, 내 의사를 잘 표현 못했다. 그 상황을 넘어가고자 그냥 네네 하고 내 코드를 리팩토링을 했던거 같다. 납득이 안가는 상황이 너무 많았고 프로젝트를 시작할 때 프로젝트에서 구현 해야되는 것을 서로 잘못 이해했던 것도 크게 작용했던거 같다.

  ★ 결론 : 내가 의도 했던 것이 상대 의도와 같다고 생각하면 안될것 같다.

 

◎ 우선순위 선정 필요 서비스가 구현되기 위해 필요한 기능들을 우선시 해야 하는데, 자기가 한 일이 끝났다고 다른사람의 일을 간과하면 안될 것 같다. 우리 팀은 서비스를 구현하기 위해 가장 중요한 것은 Mathpix에서 사진을 읽어 들여서 틀린 곳을 찾아서 표현해주는 것인데 Mathpix를 담당자가 계속 막혔는데... 각자 할 일만했다. 결국 발표 시간 도중에 결과를 출력할 수 있었다. kickoff를 하면서 코드스테이츠 엔지니어가 처음에 했던 말이었는데 너무 간과했다.

  ★ 결론 : 우선 서비스를 구현 시키는게 먼저고, 거기에 살을 붙여서 서비스의 완성도를 높여야 한다.

 

#codestates#2주프로젝트#후기#백서#puri

728x90
반응형

'부트캠프 > Codestates' 카테고리의 다른 글

4주 프로젝트 백서  (3) 2020.09.27
4주 프로젝트 중간 후기  (0) 2020.09.05
Full time Immersive 11주 후기  (0) 2020.08.21
Full time Immersive 9주 후기  (0) 2020.08.09