All Articles

React에서 자식 컴포넌트끼리의 데이터 교환


2차 프로젝트 때의 기억을 더듬어 써보는 포스팅.. 코드 엉성함 주의 😂😂😂

image.png

sibling component간 데이터 교환

일반적으로 우리가 부모 component에서 자식 component로 데이터를 보낼땐 props를 이용한다.
하지만, 반대로 자식에서 부모에게 데이터를 보낼 수 있는 것일까?!
정통(?)적으론 힘든 일이라서 리덕스나 컨텍스트같은 개념들이 나왔겠지..?
하지만 우리의 리액트세상에 불가능이란 없다!! 할 수는 있다 ㅋㅋ

콜백함수

그때도 지금도 나에겐 아직 넘나 어색한 콜백함수를 이용하면 자식에서 부모로 정보를 넘길 수 있다.
그렇다면 콜백함수란 뭘까?
바로바로!! 함수를 인자로 사용하는 함수를 말한다.

예시를 통해 살펴보자…

image.png

2차 프로젝트인 애드워드 프로젝트에서 골치 아팠던 것 중 하나..
동영상 업로드 버튼을 누르면 모달창이 뜨는데!

(막판에 급하게 모바일 버전으로 수정하느라 css가 깨진건 무시해주세요 ㅋㅋ)

image.png

왼쪽에 광고영상 업로드, 퀴즈 업로드라는 두개의 탭이 있고, 각각의 탭을 누를 경우 오른쪽 빈 공간에 해당 탭의 컴포넌트를 불러오는 식이다.

근데 우리 프로젝트 특성상, 광고영상 고유의 id가 있어야 했고, 그 아이디는 광고정보를 ‘POST’로 보냈을 때 모든 정보가 유효하면 백앤드쪽에서 넘어 오는 id였고, 나는 해당하는 id를 퀴즈 업로드 시 ‘POST’로 같이 부쳐줘야 했다!

이틀을 고민해도 답이 안 나왔는데.. 동기 사랑~ 나라 사랑~~ 광훈님의 도움으로😆 콜백함수를 이용하게 되었다!

일단.. 코드를 보기 전 내 컴포넌트 구조를 다시 한번 살펴보자면!

image.png

ModalForm이라는 부모 컴포넌트 하에 AdVideoForm과 AdQuizForm이라는 자식 컴포넌트들이 있는 건데..
resId라는 값을 자식=>부모=>자식 컴포넌트 순으로 전달을 해야 하는 것!

일단 부모 컴포넌트에 콜백함수를 작성한다.

// ModalForm.js(부모 컴포넌트)
class ModalForm extends Component {
  constructor() {
    super();
    this.state = {
      activeTab: "videoTab",
      visible: true,
      resId: 0
    };
  }

  onUpdateResId = resId => {
    this.setState({
      resId: resId
    });
  };

  switchMenu = activeTab => {
    this.setState({ activeTab: activeTab });
  };

  onClose = () => {
    const { visible } = this.state;
    if (visible === !!visible) {
      this.setState({
        visible: false
      });
    } else {
      this.setState({
        visible: true
      });
    }
  };

  render() {
    let subView = [
      { videoTab: <AdVideoForm onUpdateResId={this.onUpdateResId} key="0" /> },
      { quizTab: <AdQuizForm key="1" resId={this.state.resId} /> }
    ];

    return (
      <ModalBackground canSee={this.visible}>
        <ModalWrapper>
          <TitleWrapper>
            <TitleLetters>Adwards</TitleLetters>
          </TitleWrapper>
          <AdVideoWrapper>
            <AdLeft>
              <LeftUL>
                <AdSideLILef
                  active={this.state.activeTab === "videoTab"}
                  onClick={() => this.switchMenu("videoTab")}
                >
                  <Span>광고 영상 업로드</Span>
                </AdSideLILef>
                <AdSideLIRig
                  active={this.state.activeTab === "quizTab"}
                  onClick={() => this.switchMenu("quizTab")}
                >
                  <Span>퀴즈 업로드</Span>
                </AdSideLIRig>
              </LeftUL>
            </AdLeft>
            <AdRight>
              {subView.map((el, key) => {
                el.key = key;
                return el[this.state.activeTab];
              })}
            </AdRight>
          </AdVideoWrapper>
          <CloseOK>
            <CloseLetter cancel onClick={this.onClose}>
              취소
            </CloseLetter>
            <Link to="/biz_main" style={{ textDecoration: "none" }}>
              <CloseLetter>확인</CloseLetter>
            </Link>
          </CloseOK>
        </ModalWrapper>
      </ModalBackground>
    );
  }
}

하나하나 코드를 다시 뜯어보자면

image.png

먼저 부모 컴포넌트의 state에 resId를 0으로 초기화 시켜주고..
onUpdateResId 라는 함수를 만들고, 들어오는 값과 동일한 값으로 state값을 업데이트 시키기

이제 자식컴포넌트로 가서…

// AdVideoForm(resId를 받는 자식컴포넌트)
// .... 중략..
  onClickSave = e => {
    const {
      videoTitle,
      videoLink,
      videoThumbnail,
      videoCate,
      videoDesc,
      videoBudget,
      videoPrice,
      tags,
      videoInterests
    } = this.state;

    const title = videoTitle.trim();
    const link = videoLink.trim();
    const thumbnail = videoThumbnail.trim();
    const desc = videoDesc.trim();
    const cate = videoCate;
    const budget = videoBudget.trim();
    const price = videoPrice.trim();
    const interests = videoInterests;
    const tag = tags;

    if (
      title === "" ||
      link === "" ||
      thumbnail === "" ||
      desc === "" ||
      cate === "" ||
      budget === "" ||
      price === ""
    ) {
      alert("모든 칸을 채워주세요");
    } else {
      fetch("http://13.125.254.18:8000/advertisement", {
        method: "POST",
        headers: {
          Authorization: `${AuthAd}`
        },
        body: JSON.stringify({
          title: title,
          video_link: link,
          thumbnail: thumbnail,
          ad_category_id: cate,
          description: desc,
          budget: budget,
          price_per_view: price,
          tag: tag,
          interests_type_id: interests
        })
      })
        .then(response => {
          if (response.status !== 200) {
            return response.json();
          } else {
            alert("영상 정보가 저장되었습니다.");
            return response.json();
          }
        })
        .then(response => {
          this.props.onUpdateResId(response);
          this.setState({
            resId: response
          });
        });
  // 중략...

다시 코드를 하나 하나 뜯어보자! 씹고 뜯고 맛보고 즐기고~😜

image.png

아까와 마찬가지로 resId를 state안에 넣어서 초기화 해주고..

image.png

유저가 입력한 정보가 서버로 잘 보내지면 response에서 resId가 넘어오는데 여기서 부모컴포넌트에 썼던 onUpdateResId라는 함수를 props로 넘겨서 response를 받는다.

image.png

개발자도구-네트워크 탭을 확인해보면 값이 아주 잘 들어왔다!
이렇게 resId를 업데이트 해주고 다시 부모컴포넌트로 돌아가서..

image.png

response를 통해 받은 값을 콜백함수를 통해 resId에 담아서 업데이트를 해주었다!
이제 이 업데이트한 값을 또 다른 자식 컴포넌트로 보내야 하는데..

image.png

바로 아래에 또다른 자식컴포넌트를 부르는 탭이 있어서
저기다가 바로 resId를 부모 컴포넌트의 state의 resId라고 대입을 해준다!

이제 AdQuizForm 컴포넌트로 넘어가보자.

  onSaveQuiz = e => {
    const {
      quizQuestion,
      quizRightAnswer,
      testOne,
      testTwo,
      testThree,
      testFour
    } = this.state;
    const question = quizQuestion.trim();
    question.trim();
    if (
      question === "" ||
      testOne === "" ||
      testTwo === "" ||
      testThree === "" ||
      testFour === ""
    ) {
      alert("모든 칸을 채워주세요");
    } else {
      fetch("http://13.125.254.18:8000/quiz", {
        method: "POST",
        headers: {
          Authorization: `${AuthAd}`
        },
        body: JSON.stringify({
          ad_id: this.props.resId.advertisement_id,
          quizzes: [
            {
              content: quizQuestion,
              choices: [testOne, testTwo, testThree, testFour],
              answers: quizRightAnswer
            }
          ]
        })
      }).then(response => {
        if (response.status !== 200) {
          return response.json();
        } else {
          alert("퀴즈 정보가 저장되었습니다.");
        }
      });
    }
  };

자식=>부모로 넘기는게 진짜 힘든 과정이었는데 이제 정말 다 왔다!
부모=>자식으로 넘기는건 걍 props 쓰면 되니까 넘나 간단한 것~

확인차.. render 밑에다 console.log(this.props, ‘props)를 찍어보았다.

image.png

아주 잘 찍히고 있다! 서버로 보낼때는 adid라는 이름으로 보내야 하기 때문에
Body에 `ad
id: this.props.resId.advertisement_id`라는 값을 실어준다. 그럼 해결쓰!

이걸 해결하고 나서의 교훈..

  1. 구조는 최대한 간단하게 짜자. depth가 깊어지면 쥬금…ㅠㅠ
  2. 리덕스를 공부하자.

애초에 리덕스를 공부했으면 이걸로 고민했을 일이 없는 거였는데! 리덕스 안 쓰고 쌩으로 하려니 넘나 힘들었던 것… 기술은 괜히 발전하는게 아니다 ㅋㅋㅋㅋ