본문 바로가기

Vue.js

[Udemy Vue 완벽가이드 Section16] 252. PUT요청(HTTP) 전송으로 코치 데이터 저장하기

# 백엔드 서버 없이 사용한 경우

지금까지 작업한 데이터는 모두 더미 데이터 → 영구적이지 않다.

코치를 등록했지만, 앱을 새로고침하면 사라진다. 즉, 데이터가 손실된다.

또한, 데이터를 이 컴퓨터에서만 확인할 수 있다. 다른 사용자의 데이터는 못본다.

당연히..앱은.. 이런 식이면 안된다.

전 세계 모든 코치의 정보를 제공하는게 목적이므로.

 

그래서 HTTP요청과 데이터를 저장할 수 있는 백엔드 서버가 필요하다.

이번에도 Firebase를 사용하는데, 유일한 옵션이기 때문은 아니다.

서버 사이드 코드를 작성하지 않아도 되고, 무료로 시작할 수 있다는 것도 장점.

(물론 실제 프로젝트에서 사용할 때는 요금 페이지를 확인해야한다.)

 

새 프로젝트를 생성하고, Realtime Database로 가서 이 데이터베이스가 사용되는지 확인한다.

데이터베이스가 생성되면 규칙이 이런식으로 되어있는지 확인해야한다.

값이 false나 다른 값이어서는 안되고, 이런 모습이어야 한다.

{
  "rules": {
    ".read": "now < 1696518000000",  // 2023-10-6
    ".write": "now < 1696518000000",  // 2023-10-6
  }
}

 

이 작업이 완료되면 이 주소를 Vue 앱 내부에서 요청을 보내는 백엔드로 사용할 수 있다.

 

그럼 이제 어디로 어떤 요청을 보내는지 알아보자!

앱에서 코치로 등록할 때, form 작성을 완료하는 즉시, 등록 요청을 보낸다.

 

 

모든 코치의 정보를 fetching하는 코치 목록 페이지에서는 "Refresh 버튼"을 누를 때마다 데이터를 다시 fetching 한다.

 

Requests 페이지에서는 모든 요청을 fetching한다.

 

 

Contact 양식이 제출될 때는 요청을 보내야한다.

이는 Vuex 저장소와 일치한다.

coaches폴더에서는 registerCoach 액션에서 "coach 생성 요청"을 보내고, 

requests폴더에서는 contactCoach가 "request 추가 요청"을 서버로 보낸다.

 

coaches의 actions.js의 registerCoach에서 coachData 객체를 생성하고 있다.

// coaches > actions.js
export default {
  registerCoach(context, data) {
    const coachData = {
      id: context.rootGetters.userId,
      firstName: data.first,
      lastName: data.last,
      areas: data.areas,
      description: data.desc,
      hourlyRate: data.rate,
    };
    context.commit('registerCoach', coachData);
  },
};

이 coachData 객체를 Firebase로 보낸다.

→ 브라우저에 내장된 fetch함수로 작업을 수행할 수 있다.

export default {
  registerCoach(context, data) {
    const coachData = {
      id: context.rootGetters.userId,
      firstName: data.first,
      lastName: data.last,
      areas: data.areas,
      description: data.desc,
      hourlyRate: data.rate,
    };
    
    fetch('https://vue-http-demo-aee58-default-rtdb.firebaseio.com/coaches.json');
    
    context.commit('registerCoach', coachData);
  },
};

 

fetch함수에 문자열, 즉 요청을 보내야 하는 URL을 인수로 받는다.

(URL은 Realtime Database에 있는 URL이다.)

 

뒤에 '/coaches'와 같은 세그먼트를 추가 + Firebase이므로 끝에 .json을 붙여준다.(Vue라서 .json을 붙인건 아니다.)


이 URL로 요청이 전송된다.

보통, 기본 요청은 GET요청이다.
GET요청은 데이터를 가져오는데 사용되지만, 여기서는 coachData를 저장하는데 사용할 예정이다.

두 번째 인수 필요 : 보내려는 요청의 종류를 구성하는 객체를 전달. 

 

method키를 'POST'로 설정해도 되지만, 한가지 가정이 필요하므로 그렇게 하고싶지 않다.

코치 ID는 코치로 등록하기 전부터 존재한다는 가정이다.

나중에 앱에 인증기능을 추가하면 코치로 등록하기위해 먼저 가입부터 해야한다.

가입 후, 코치로 등록. 따라서 코치가 되려는 사용자 id가 이미 존재한다고 가정할 수 있다.

실제 지금도 root store의 state에 더미 사용자id가 있다. (userId: 'c3')

나중에 인증을 추가하면 실제 id로 바뀌겠지만, 지금은 더미 id이다.

// index.js
import { createStore } from 'vuex';
import coachesModule from './module/coaches/index.js';
import requestsModule from './module/requests/index.js';

const store = createStore({
  modules: {
    coaches: coachesModule,
    requests: requestsModule,
  },
  state() {
    return {
      userId: 'c3',
    };
  },
  getters: {
    userId(state) {
      return state.userId;
    },
  },
});

export default store;

 

 

actions.js 파일에서 이미 id를 가져왔다.

coachData에 id를 가져오지 않고, 대신 userId라는 새로운 상수에 저장해보자.

 

그리고 "/coaches/{userId}"를 가리키도록 URL을 수정해, Firebase에 이 사용자를 위한 별개의 엔트리를 만들어준다.

userId를 문자열에 편리하게 추가하기 위해 JS기능을 사용해보자.

`${}`구문을 사용하면 문자열에 데이터를 주입할 수 있다.

그리고 PUT요청을 보내 데이터가 존재하면 덮어쓰고, 존재하지 않으면 생성하라고 Firebase에 알려준다.

export default {
  registerCoach(context, data) {
    const userId = context.rootGetters.userId;
    const coachData = {
     // id: context.rootGetters.userId,
      firstName: data.first,
      lastName: data.last,
      areas: data.areas,
      description: data.desc,
      hourlyRate: data.rate,
    };
    
    fetch(`https://vue-http-demo-aee58-default-rtdb.firebaseio.com/coaches/${userId}.json`, {
      method: 'PUT',
      body: JSON.stringify(coachData)
    });
     
    context.commit('registerCoach', coachData);
  },
};

 

** POST요청 vs PUT요청

"PUT"요청과 달리 "POST"요청은 항상 새 엔트리를 추가한다.

여기서 원하는 기능은 아니다.

사용자당 엔트리를 하나만 할당해야하므로.

 

fetch함수로 PUT요청을 보낼 시, body도 필요하다.

→ JSON.stringify를 사용해 "JSON 문자열로 변환한 coachData"를 첨부한다.

이렇게 하면 coachData객체를 JSON형식으로 변환해 요청에 첨부해 전송할 수 있다.

fetch함수는 데이터를 첨부해 이 URL로 PUT요청을 보낸다.

이제 이 작업이 완료된 후에 다음 단계로 넘어가게 설정하자.

 

 

fetch는 promise객체를 반환한다.

따라서 then을 사용하여 요청이 완료되면 즉, promise객체가 반환되면 실행할 코드를 정의할 수 있다.

 

더 모던한 async/await구문을 사용하는 방법도 있다.

promise가 없는 일반 코드처럼 보이게 하면서도 내부적으로는 promise를 사용.

 

먼저 메서드 이름 앞에 async 키워드를 추가한 다음, promise, 즉 fetch함수의 호출결과 앞에 await 키워드를 붙여준다.

그리고 호출 결과를 일반 변수나 상수에 저장한다. (물론 결과는 이 작업이 완료된 경우에만 저장된다.)

여기서 response를 then 블록에 넣는 것과 비슷하다.

//actions.js
export default {
  async registerCoach(context, data) {
    const userId = context.rootGetters.userId;
    const coachData = {
     // id: context.rootGetters.userId,
      firstName: data.first,
      lastName: data.last,
      areas: data.areas,
      description: data.desc,
      hourlyRate: data.rate,
    };
    
   const response = await fetch(`https://vue-http-demo-aee58-default-rtdb.firebaseio.com/coaches/${userId}.json`, {
      method: 'PUT',
      body: JSON.stringify(coachData)
    });
    
    const responseData = await response.json();
    if(!response.ok)  {
     // error ...
    }
     
    context.commit('registerCoach', {
      ...coachData,
      id: userId,
    });
  },
};

요청에 대한 응답을 설정했다.

다음 줄에 들어갈 코드(const responseDate부터..)는 위 await fetch.. 요청이 완료된 후에만 실행된다.

 

 

응답이 올테니 response.json()을 대기했다가, 파싱된 responseData를 얻을 수 있다.

그런 다음 if검사를 실행해 response가 ok인지 확인한다.

response 객체에는 응답이 정상인지 아닌지를 나타내는 ok필드가 있다.

그리고 오류가 발생할 경우 실행할 코드를 작성한다.(추후에..)

일단은 오류없이 항상 잘 작동한다고 가정하자.

 

그리고 데이터를 커밋한다(context.commit..)

다른 데이터를 commit한다.

spread연산자를 사용해 coachData를 복사하는 객체를 커밋한다.

coachData의 모든 key-value쌍을 가져와 새 객체에 병합한다. 그리고 userId를 입력할 수 있는 id필드를 추가한다.

 

coachData를 서버로 보내지 않아도 된다. 이미 해당 id로 서버에 있는 별도의 노드에 저장했기 때문.

따라서 id를 두 번 사용하는 셈이다.

 

하지만 로컬에 commit한 데이터에 id를 추가해주면 Vuex에 있는 데이터 객체에서 코치의 id를 빠르게 사용할 수 있다.

commit은 전처럼 작동할 것이고, 모든 promise가 반환된 후에 실행될 것이다.

responseData로는 아무것도 하지 않으니 삭제해도 된다. 여기서는 주석처리.

 

이제 코치로 등록하면, 데이터가 서버에 저장되어야 한다.

Firebase를 보면 coaches 노드에 더미 코치id가 있다.

그 아래에는 이 코치에 속한 데이터가 들어있다.

개선할 점도 있다.

한가지 예를 들면, coach를 등록하고, coache 리스트화면으로 리디렉션될때 새로 등록한 코치 정보가 뜨는데에 약간의 지연이 있다.

게다가 코치 데이터를 제대로 fetching하고 있지 않다.

앱을 새로고침하면 다시 더미 코치만 표시된다.

 

** 출처: 모든 내용은 Udemy Vue-완벽가이드 강의를 기반으로 작성하였습니다.