
작성한 프로그래밍이 엉망진창이지만 의도대로 동작한 프로그램 생각보다 많죠? 사실 저도 제가 코드를 작성할땐 이게 최선의 코드라고 생각했지만, 지적해줄 시니어가 없다던가, 되돌아 봤을때 아니였던 경우가 은근 많죠.
외부에서 비개발자나 다른파트 개발자가 볼땐 정상동작하는 것 처럼 보이지만.. 내부는 많이 곪아있는 케이스 이런 케이스는 사실 어디서나 등장하지 않을까요??
그게 뭐? 동작하기만 하면 됐지!
import React, { useRef } from "react"; const ForwardRef = () => { const inputRef = useRef<HTMLInputElement>(null); const clickHandler = () => { inputRef.current?.focus(); }; return ( <> <input type="text" ref={inputRef} /> <button type="button" onClick={clickHandler}> Focus </button> </> ); }; export default ForwardRef;
위와 같은 코드가 있다고 가정해봅시다. 아주 간단한 코드고, 해석하자면 ‘Focus 버튼을 누르면 input에 포커스효과를 준다.’ 입니다.
이런 간단한 코드가 있는데, 만일 저
<input type="text" ref={inputRef} />
가 컴포넌트로 분리될때 어떻게 처리해야할까요?import React, { useRef } from "react"; const Input = ({ ref }: { ref: React.RefObject<HTMLInputElement> }) => <input type="text" ref={ref} />; const ForwardRef = () => { const inputRef = useRef<HTMLInputElement>(null); const clickHandler = () => { inputRef.current?.focus(); }; return ( <> <Input ref={inputRef} /> <button type="button" onClick={clickHandler}> Focus </button> </> ); }; export default ForwardRef;
과연 이 위 코드는 정상적으로 동작할까요?

위 코드는 정상적으로 동작하는 것 처럼 따로 IDE에서 에러가 나진 않지만 콘솔에서 다음과 같은 에러를 확인할 수 있습니다.
Warning: Input:ref
is not a prop. Trying to access it will result inundefined
being returned. If you need to access the same value within the child component, you should pass it as a different prop.
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
충격 ref는 prop 가 아니랍니다. 왜?
{ ref }: { ref: React.RefObject<HTMLInputElement> }
우리는 분명 ref의 타입을 지정했고, 정해진 타입대로 Input 컴포넌트에 넣어주었는데 말입니다..위와같은 에러사항을 마주했던 다른 개발자는 차선책을 선택했습니다.
import React, { useRef } from "react"; const Input = ({ inputRef }: { inputRef: React.RefObject<HTMLInputElement> }) => <input type="text" ref={inputRef} />; const ForwardRef = () => { const inputRef = useRef<HTMLInputElement>(null); const clickHandler = () => { inputRef.current?.focus(); }; return ( <> <Input inputRef={inputRef} /> <button type="button" onClick={clickHandler}> Focus </button> </> ); }; export default ForwardRef;
위처럼 말이죠, ‘ref가 prop가 아니라면 네이밍을 변경하면 되지!’ 라는 생각으로 말이죠.
사실 크게 문제될 건 없어보입니다. inputRef도 똑같은 ref역할을 하고, prop로 받은 inputRef를 ref에 넣어주기 때문입니다.
하지만 위에 나온 에러를 제대로 읽어본 사람이라면 다른 해결책을 제시할 수 있을겁니다.
Did you mean to use React.forwardRef()?
우리는 항상 에러로그를 잘 봐야합니다.그렇습니다. 위 내용에서 크게 문제될 것 같진 않지만 있는 ref prop를 사용하면 되지 뭣하러 우회를 해야하는가.. 그렇기 때문에 위에서 제시하는 forwardRef가 뭔지.. 어떻게 써야하는지에 대해 알아보고자 합니다.
forwardRef로 참조하기
forwardRef의 공식문서를 보면 다음과 같이 설명하고 있습니다.
forwardRef
lets your component expose a DOM node to parent component with a ref.
(번역본) ForwardRef를 사용하면 구성 요소가 참조를 사용하여 DOM 노드를 상위 구성 요소에 노출할 수 있습니다.
우리가 딱 필요로하는 ref의 역할이 적혀있습니다. 상위 구성 요소에 우리가 작성한
<Input />
을 노출시켜야 합니다. 사용방법은 매우 간단합니다. ref를 참조시키고 싶은 노드를 forwardRef로 묶으면 됩니다.
const SomeComponent = forwardRef(render)
그럼 우리가 해야하는 것은 ref를 상속받을 Input 컴포넌트에 forwardRef를 붙여주는 것입니다.
import React, {forwardRef, useRef} from "react"; const Input = forwardRef((props, ref: React.ForwardedRef<HTMLInputElement>) => <input type="text" ref={ref} />) const ForwardRef = () => { const inputRef = useRef<HTMLInputElement>(null); const clickHandler = () => { inputRef.current?.focus(); }; return ( <> <Input ref={inputRef} /> <button type="button" onClick={clickHandler}> Focus </button> </> ); }; export default ForwardRef;
이렇게 Input에 forwardRef를 활용하면서 자식에게 ref속성을 줄 수 있습니다. 이렇게 사용하면 우리는 이제 부모에서 자식의 노드에 접근하기 용이해졌습니다.
우회할 수 있는 방법은 언제나 있지만, 조금이라도 깨끗한 코드를 작성하는데 있어서 필요한 지식이라고 생각합니다.
지금 회사 프로젝트에서도 layer 위에 다른 layer를 띄울때나, dropdown, meatballs을 띄울때 해당 ref 이외 영역을 클릭 시 해당 layer를 종료하는 코드를 작성할때도 부모에서 자식으로 ref를 내려주곤 합니다. 분명 알아두면 생각보다 쓸 곳이 많이 있을 겁니다.
코드는 언제나 간결할수록, 알아보기 쉽도록 구현하는것이 중요할 것입니다. ref 와 inputRef 처럼 같은 의미로 사용되지만 네이밍을 다르게 가져간다면 동작하는 웹이 될 순 있어도 완벽한 하나의 웹이 되진 못할 것입니다.
별거 없는 글 봐주셔서 감사합니다 :D 다음엔 더 알찬 내용으로 오도록 하겠습니다!