Q.

input태그에서 onChange를 했을 때, event handler 함수가 다 비슷하거든요. 혹시 해당 함수를 공통화시켜서 코드를 확 줄일 수 있는 방법 없을까요??
render 함수 내부 코드는 다음과 같습니다.

<input
  name="picture1"
  onChange={this.handleImageChange1}
  className="imageUploadBtn"
  required
/>

<input
  name="picture2"
  onChange={this.handleImageChange2}
  className="imageUploadBtn"
  required
/>

<input
  name="picture3"
  onChange={this.handleImageChange3}
  className="imageUploadBtn"
  required
/>

함수 정의 부분 코드는 다음과 같습니다.

  handleImageChange1 = (event) => {
    event.preventDefault();
    let reader1 = new FileReader();
    let BnBImg1 = event.target.files[0];

    reader1.onloadend = () => {
        this.setState({
            BnBImg1: BnBImg1,
            imagePreviewUrl1: reader1.result,
            isThereImage1: 'block'
        })
        console.log('reader1.result', reader1.result);
    }
    reader1.readAsDataURL(BnBImg2)
  }

  handleImageChange2 = (event) => {
    event.preventDefault();
    let reader2 = new FileReader();
    let BnBImg2 = event.target.files[0];

    reader2.onloadend = () => {
        this.setState({
            BnBImg2: BnBImg2,
            imagePreviewUrl2: reader2.result,
            isThereImage2: 'block'
        })
        console.log('reader2.result', reader2.result);
    }
    reader2.readAsDataURL(BnBImg2)
  }

  handleImageChange3 = (event) => {
    event.preventDefault();
    let reader3 = new FileReader();
    let BnBImg3 = event.target.files[0];

    reader3.onloadend = () => {
        console.log('나와랑', reader3);
        console.log('나와랑2', BnBImg3)

        this.setState({
            BnBImg3: BnBImg3,
            imagePreviewUrl3: reader3.result,
            isThereImage3: 'block'
        })
        console.log('reader3.result', reader3.result);
    }
    reader3.readAsDataURL(BnBImg3)
  }

그리고 함수를 공통화 시키려면, 왠지 지금 바뀐 요소가 1, 2, 3 중에 어떤 input 태그인지 인자같은 걸로 넘겨줘야할것 같은데…
onchange에는 함수의 정의나 함수 이름만 전달해야 되던데…? 인자를 도대체 어떻게 넘길 수 있는건가요?

A.

일단 본격적으로 질문에 대한 답을 설명하기 전에 정리하고 넘어갈 게 있습니다.
해당 함수 내에서 불러오고자 하는 FileReader 함수를 reader로 선언하고자 하는 상황이예요.
reader2, reader3, reader4 이런 식으로 따로 선언할 필요가 없습니다.
왤까요????? 어차피 선언한 내용은 scope에 막히기 때문입니다.

let reader = new FileReader();

그냥 이렇게 선언하도록 하겠습니다.

같은 이유로, ★★ 해당 onChange ★★에 대한 event 를 인자로 받아서 ★★ 해당 event ★★의 event.target.files[0]을 각 함수 내에서 사용하고자 하는 거니, BnBImg2, BnBImg3, BnBImg4 도 각각의 이름을 다르게 선언할 필요가 없습니다.

let BnBImg = event.target.files[0];

따라서 이렇게 바꾸도록 하죠!!

이렇게 바꾸고 나면 각 함수의 기본적인 형태는 이렇게 변합니다.

  handleImageChange2 = (event) => {
    event.preventDefault();
    let reader = new FileReader();
    let BnBImg = event.target.files[0];

    reader.onloadend = () => {
        this.setState({
            BnBImg2: BnBImg,
            imagePreviewUrl2: reader.result,
            isThereImage2: 'block'
        })
        console.log('reader.result', reader.result);
    }
    reader.readAsDataURL(BnBImg)
  }

이제 다른거라곤, setState 안에 들어있는 key값인 BnBImg2, imagePrevieUrl2, isThereImage2뿐입니다.
게다가 뒤에 붙은 숫자만 2, 3, 4로 다른데, 변수로 관리해줄수 있지 않을까요?!!??

그런데… 변수를 어디서 받아오죠? 각 input태그의 onChange에서 변수를 보내줘야 할텐데… 보내줄 수 있는 방법이 있을까요?????

<input
  name="picture2"
  onChange={this.handleImageChange2}
  className="imageUploadBtn"
  required
/>

여기서 말이에요…!!

네!!! 있습니다!!!

현재는 onChange event handler가 onChange에 걸리는 함수에 event 하나의 인자만을 첫 번째 인자로 전달해줍니다. 이게 무슨 말이냐구요???

onChange, onClick과 같은 경우에는 우측에 함수 실행문을 넣지 않고 정의만 넣어줬었죠??
그러면 onClickonChange가 그 함수도 자동으로 실행시켜주고, onClickonChange 이벤트에서 받은 event를 해당 함수의 ★★★ 첫 번째 인자★★★로 넘겨줍니다!!!!!

<input
  name="picture2"
  onChange={(event) => this.handleImageChange2(event)}
  className="imageUploadBtn"
  required
/>

원래는 이런 형태였다는 뜻이죠!! event 를 받아와서, this.handleImageChange2라는 함수에 event 를 첫 번째 인자로 넘겨준다!!

그런데 제가 왜 ‘첫 번째 인자’라는 표현을 썼을까요…?
당연한 거지만… 우리가 코딩을 하고있긴 해도, 이건 기본적으로 함수기 때문에!! 두 번째 인자와 세 번째 인자에 꼭 넘겨줘야만 하는 값이 함수 자체에 정의되어 있지 않다면, 우리가 임의로 두 번째 인자, 세 번째 인자를 설정해서 넘겨줄 수 있다는 소리이기 때문입니다~~~

그렇다면… 해당 함수에서 특정 숫자(2, 3, 4)를 가져다 쓸 수 있도록 input태그의 onChange attribute에서 넘겨줄 수 있지 않을까요?!?!??!?!?!??

<input
  name="picture2"
  onChange={(event) => this.handleImageChange2(event, 2)}
  className="imageUploadBtn"
  required
/>

이렇게 말입니다!!!!!!!
이렇게 숫자를 넘겨줬다면, 위의 함수에서도 그걸 받아줘야겠죠??

  handleImageChange2 = (event, number) => {
    event.preventDefault();
    let reader = new FileReader();
    let BnBImg = event.target.files[0];

    reader.onloadend = () => {
        this.setState({
            BnBImg2: BnBImg,
            imagePreviewUrl2: reader.result,
            isThereImage2: 'block'
        })
        console.log('reader.result', reader.result);
    }
    reader.readAsDataURL(BnBImg)
  }

맨 위를 보세요!! event외에 number라는 것도 인자로 받아올거야~ 라는 것을 함수에 알려주었습니당.

이제 number를 인자로 받아왔으니, 함수 내부에서 사용할 수 있게 되었어요!!!
number인자를 써줘야 하는 곳은 setState 함수 안의 객체 안의 key값입니다.(;;;;;;;;;;)

객체의 key값에 변수를 넣는다…………. 변수를 그냥 쓸 수 있던가요?
아닙니다 ㅎ… 제가 이걸 엄청 많이 틀려서 예리님에게 호되게 혼났었는데요…
예리님은 이 필기 내용이 제 메모장의 어느 부분에 있는지도 알고 계시답니다…

enter image description here

객체의 프로퍼티명에 접근할 때 어떻게 접근하는지, 모두 알고 계시죠??
보통 .으로 접근하지만, 대괄호를 써야만 하는 때가 있었습니다.
대괄호 안에는 변수가 들어갈 수 있어요…

자, 그러면 우리 대괄호를 써서 key를 다시 써보도록 합시다!!

  handleImageChange2 = (event, number) => {
    event.preventDefault();
    let reader = new FileReader();
    let BnBImg = event.target.files[0];

    reader.onloadend = () => {
        this.setState({
            ['BnBImg' + number]: BnBImg,
            ['imagePreviewUrl' + number]: reader.result,
            ['isThereImage' + number]: 'block'
        })
        console.log('reader.result', reader.result);
    }
    reader.readAsDataURL(BnBImg)
  }

대괄호 안에서 string과, 인자로 받아온 변수를 조합해 key값을 완성했습니다!

이제 함수 이름은 하나로 통일해도 되겠죠?

  handleImageChange = (event, number) => {
    event.preventDefault();
    let reader = new FileReader();
    let BnBImg = event.target.files[0];

    reader.onloadend = () => {
        this.setState({
            ['BnBImg' + number]: BnBImg,
            ['imagePreviewUrl' + number]: reader.result,
            ['isThereImage' + number]: 'block'
        })
        console.log('reader.result', reader.result);
    }
    reader.readAsDataURL(BnBImg)
  }

handleImageChange라는 하나의 함수로 만들어주었습니다. 함수명이 바뀌었으니, onChange에 걸려있는 함수명도 바꿔주는 걸 잊지 맙시다!!!!!!!!

<input
  name="picture2"
  onChange={(event) => this.handleImageChange(event, 2)}
  className="imageUploadBtn"
  required
/>

<input
  name="picture3"
  onChange={(event) => this.handleImageChange(event, 3)}
  className="imageUploadBtn"
  required
/>

<input
  name="picture4"
  onChange={(event) => this.handleImageChange(event, 4)}
  className="imageUploadBtn"
  required
/>