WIL | 미니 프로젝트 완료 | 비동기와 동기

isthis·2021년 11월 7일
0
post-thumbnail

미니 프로젝트 완료!!

드디어 1주 차 프로젝트가 마무리되었다.
우여곡절이 많은 한 주였다,,
하지만, 우여곡절 끝에 프로젝트를 마무리했을 때의 기쁨은 이루 말할 수가 없다.

https://blog.naver.com/rjsgmldnwn/222561254473
첫 프로젝트 소감문(?)
https://github.com/dev-sohye/hanghae99_11
결과물

그간의 우여곡절과 배운 점을 정리해 보자.


프로젝트 과정

  1. 내가 맡은 역할은 로그인, 회원가입 기능이었다.
  2. 기능 구현 완료 후, 팀원들과 구역을 나눠 페이지 디자인을 마무리했다.
  3. 마지막으로 미제 기능을 봤다.

비동기와 동기

(비동기와 동기를 알아본 과정.)

요구사항

때는 프로젝트가 마무리돼가던 단계였다. 디자인을 마무리한 후 미제 기능이었던 댓글 삭제로 돌아왔다.(하지만 결국 프로젝트 완료 후 기능 구현했다.)
기존에 배웠던 방법은 삭제 버튼을 눌렀을 때 전체 댓글 중 키워드가 포함돼 있는 댓글을 삭제하는 것이었다.
하지만, 우리는 댓글 창에 여러 사람이 작성할 수 있고 댓글마다 키워드가 겹칠 수 있기 때문에 그 방법을 적용시키기에는 문제가 있었다. 따라서 다음과 같은 조건을 정했다.

  • 본인이 작성한 것만 삭제해야 함
  • 중복된 내용이 있어도 선택한 댓글만 삭제해야 함

그래서 짠 흐름이다.

리뷰 작성 시 유저 Id와 랜덤 부여 값을 댓글DB에 등록한다.
삭제 버튼 클릭 시 해당 리뷰의 랜덤 부여 값 받아온다.
토큰을 통해 현재 유저 Id와 해당 리뷰를 작성한 유저 id가 같은지 확인한다.
랜덤 부여 값이 포함된 리뷰를 삭제한다.

이렇게 흐름을 구상하고 코드를 짜는데 괴상한 문제에 마주쳤다.

문제상황

//리뷰 삭제//

function deleteReview(reviewRandomId) {
          console.log(reviewRandomId);
          let userId;
          let reviewId;
          //토큰 내 유저 id정보 불러오기
          $.ajax({
              type: "GET",
              url: '/api/user',
              data: {},
              success: function (response) {
                  userId = response['user_id'];
                  console.log(userId);
              }
          });
          //리뷰 db 가져오기
          $.ajax({
              type: "GET",
              url: '/api/review',
              data: {},
              success: function (response) {
                  //선택한 리뷰 골라낸 후 유저 id 가져오기
                  for (i = 0; i < response.length; i++) {
                      let randomIdKey = response[i]['review_random_id']; //i번째 리뷰의 랜덤값
                    	//선택한 리뷰가 맞다면
                      if (randomIdKey == reviewRandomId) {
                          reviewId = response[i]['review_id']; //리뷰 작성자 id 할당
                          console.log(reviewId);
                      }
                      ;
                  }
                  ;
                  //<---------#1. 여기는 찍힌다------------>
                  console.log(reviewId, userId);
              }
          });
          //<------------#2. 여기는 안 찍힌다------------->
          console.log(userId,reviewId)
          // 선택한 리뷰 삭제하기
          //생략된 코드//
      };

console.log를 통해서 코드 중간중간 값을 확인했는데 api를 통해서 가져온 값이 맨 밑의 #1 부분에는 찍히고, #2 부분에는 찍히지 않았다. 두 부분의 차이는 #1은 ajax 내부이고, #2는 ajax 외부라는 것 뿐이다.
처음에는 #2부분에 코드를 넣어서 짰었는데 동작을 안 해서 상당히 곤란했다.(그 후에 jinja2로 뻘짓 좀 했다..)
어찌저찌 #1 부분에 코드를 넣어 기능 구현은 완료했지만, 이해가 되지 않아 알아봤다.

원인 파악

원인은 ajax가 비동기 방식이기 때문이었다.

자바스크립트엔진은 코드를 실행하면 모든 함수의 호출을 call stack이라는 구조에 담아 순차적으로 실행한다.
처음에는 call stack이 비어있다가 함수 안으로 들어가는 순간 call stack 구조 위에 불러들인 함수가 차근차근 쌓이면서 실행되는 거다.
그리고 쌓인 함수의 실행이 완료되면 call stack에서 제거하고 다음 동작을 실행한다.
다음 예를 보면 이해가 쉽다.

function multiply(x, y) {
    return x * y;
}
function printSquare(x) {
    var s = multiply(x, x);
    console.log(s);
}
printSquare(5);

call stack이 비어있는 상태에서 위 코드를 실행하면 엔진은 다음 사진과 같이 절차를 수행한다.

출처 sessionstack

위와 같이 현재 실행되는 코드가 완료되야 다음 코드를 실행하는 방식을 동기 방식이라고 한다.
문제는 여기부터다. 자바스크립트는 싱글 스레드라는 것이다. 즉, 하나의 call stack만 구성하고 하나의 작업(task)만 가능한 것.
요놈의 싱글 스레드에서 발생하는 문제점이 있다.
만약, 싱글 스레드 call stack을 통해서 함수를 호출하고 서버에서 데이터를 받아오는데 시간이 엄청 오래 걸린다고 가정해 보자. 그러면 유저는 데이터를 받아오는 동안 아무것도 할 수 없다. 페이지가 중간중간 서버 페이지로 갱신되며 깜빡거리고 로딩되기만을 기다릴 것이다(너무 길면 에러가 뜰 수도 있다.).
이러한 현상을 '블록킹'이라고 하며 이를 해결하기 위해 사용하는 방식이 바로 비동기 방식(asynchoronous)이다. 비동기 방식은 현재 실행중인 코드와 무관하게 다음 코드로 넘어가는 것을 말한다. 다음과 같은 방식으로 동작한다.

  • (Web API의)비동기 절차
  1. 자바스크립트 코드를 순차적으로 실행한다.
  2. 비동기 방식 함수를 발견하면 call stack에 넣는다.
  3. WebAPI를 호출하고 call stack에선 제거한다.
  4. WebAPI에서 처리 후 결괏값을 받는다.
  5. 결괏값 Callback을 Callback Queue에 집어넣는다.
  6. 모든 스크립트 실행이 끝난 후 call stack이 비게 되면 Callback Queue에 있는 함수를 call stack에 가져와 실행한다.(이 역할은 이벤트 루프가 수행한다.)

이벤트 루프?
call stack과 Callback Queue를 감시한다. 만약 call stack이 비어있으면 Callback Queue에서 첫 번째 이벤트를 call stack에 집어넣는다.

사실 밑에 이거 하나면 끝난다.

console.log('Hi');
setTimeout(function cb1() { 
    console.log('cb1');
}, 5000);
console.log('Bye');


출처 sessionstack

정리를 해보자면, 결국 나에게 일어난 문제는 Ajax가 비동기 방식으로 처리가 되어, Ajax내부이냐, 외부이냐에 따라 변수를 불러왔을 때의 값이 달라졌던 것이다.

결론(솔루션)

비동기 방식인 Ajax 내부의 결과값을 사용하기 위한 방법에는 크게 두 가지가 있다.

  1. 해당 Ajax 내부나 이후의 비동기 이벤트에서 사용해야한다.
  2. async-await 등을 사용하여 동기적인 Ajax요청을 하면 밖에서도 사용이 가능하다.

하지만, 2번 솔루션의 경우 위에서 말한 싱글스레드 문제점 때문에 웬만하면 사용하지 않는 것이 좋다.(인터넷을 망치는 길이라고 하는데 자세한 건 더 알아봐야겠다.)
따라서 일단 나는 1번 방법을 그대로 가져갔다.

팀 프로젝트를 하며

팀 프로젝트를 처음 해보며 어떻게 팀원들과 원활한 소통을 할 수 있을까 고민이 있었는데, 역시 가장 중요한 건 배려인 것 같다.
그리고, 다 같이 하나의 프로덕트를 만드는 것이기 때문에 현재 상황과 할 일들에 대해 공유를 잘 하는 것이 중요하다고 느꼈다.

마치며

이번 프로젝트는 아직 다들 프론트를 할지, 백엔드를 할지 미정인 상태라서 백,프론트 구분 없이 진행되었다. 기능별로 담당을 정하고, 디자인도 페이지별로 담당하여 전반적으로 맛보면서 구현하였다.
앞으로 백엔드를 공부하고, 프로젝트를 진행하면서, 백엔드와 프론트엔드가 어떻게 협업하면 좋을지 많이 배울 수 있도록 노력해야겠다.
앞으로도 화이팅!

잘못된 점이나 오타, 이상한 부분 있으면 댓글로 지적해주세요!

참고자료
sessionstack
Spicycookie
CodingRestaurant
즐거운인생

profile
공부

0개의 댓글