[React] 스크롤 이동 페이지 만들기 with react useRef(), scrollIntoView()

Bomin·2023년 10월 25일
1

[Side Project]

목록 보기
3/4

🔥 구현할 기능

What Song은 실시간 음악 스트리밍 기반 SNS로 뮤직룸에서 플레이리스트를 동시에 같이 듣는 서비스를 제공하는 프로젝트다.

그 중에서 뮤직룸 생성 기능을 스크롤 이벤트로 동작하는 페이지로 만들어보기로 했다.

구현할 기능은 다음과 같다.

기능 1) 뮤직룸 이름, 카테고리, 공개 여부를 받아서 생성버튼 누르면 POST

  • 이름 입력 후 다음 버튼 → 카테고리 선택 후 다음 버튼 → 공개 여부 선택 후 생성 버튼
  • 따라서 3가지 focus 화면(section)이 필요하다.

기능 2) 현재 화면에서 입력이나 선택되어야 다음 버튼 활성화

기능 3) 버튼이 아닌 그냥 스크롤로 이동을 해도 입력 화면으로 focus

🔆 useRef

useRef 는 .current 프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환합니다. 반환된 객체는 컴포넌트의 전 생애주기를 통해 유지될 것입니다. - React 공식 사이트

  • 저장공간이나 DOM 요소에 접근하기 위해 사용되는 React Hook이다.
  • useRef는 re-render와 상관없이 값이 유지된다는 특징이 있다.
  • 바닐라 자바스크립트에서 querySelector, getElelmentById 등을 이용하여 특정 DOM을 선택하고 조작한다.
  • 이를 React에서는 useRef 훅을 사용하여 조작할 수 있다.
    • React는 virtual DOM을 다루기 때문에 document.getElementById()으로 접근했을 때 문제가 발생할 수 있다!
    1. 리액트의 컴포넌트는 여러 개의 인스턴스를 가질 수 있다. → id가 중복될 수 있음
    2. ref를 사용하면 내가 정한 스코프에서만 접근 가능 → 전역적으로 ref가 사용되지 않기 때문에 유지보수에 용이함(단방향 데이터 흐름 방해하지 않기 때문)

👉 사용 방법

  1. useRef() 선언
const inputRef = useRef(null)
  1. 선택하고자 하는 DOM node(virtual DOM element)에 ref 속성 추가
<input ref={inputRef} type="text" />
  1. 필요한 곳에서 변수명.current 프로퍼티로 꺼내 쓰기
<button onClick={()=> inputRef.current.focus()}>
				input으로 포커스 </button>

🔆 scrollIntoView()

호출된 엘리멘트가 있는 위치로 스크롤을 이동시키는 자바스크립트 내장 메서드이다. 인자로 true/false값 또는 option 객체를 받을 수 있다.

scrollIntoView(alignToTop?, scrollIntoViewOptions?)

  • alignIntoTop
    • true : 해당 요소의 제일 상단으로 스크롤 이동, 기본 값
      • scrollIntoViewOptions: {block: "start", inline: "nearest"} 와 동일함
    • false: 해당 요소의 제일 하단으로 스크롤 이동
      • scrollIntoViewOptions: {block: "end", inline: "nearest"} 와 동일함
  • option 객체
    • { behavior: "auto"/”instant/"smooth" } : 스크롤 시 즉시 적용할지, 부드러운 애니메이션 적용할지
    • { block:"start"/"center"/"end"/"nearest" }: 수직 요소에 대한 옵션, 어느 선에서 멈출 지, default = start
    • { inline: "start"/"center"/"end"/"nearest" } , 수평 요소에 대한 옵션, 어느 선에서 멈출 지, default = nearest

🏓 구현 내용

1. 스크롤 포커스 될 섹션에 사용할 useRef 객체를 선언했다.

// 스크롤 이동을 위한 useRef 객체 선언
	const focusFirst = useRef<HTMLDivElement>(null);
	const focusSecond = useRef<HTMLDivElement>(null);
	const focusLast = useRef<HTMLDivElement>(null);

2. 다음 버튼을 누를 때 마다 스크롤 이동 해 줄 함수를 선언한다.

// 스크롤 포커스하는 함수
	const onMoveToFocus = (focus: React.RefObject<HTMLDivElement>) => {
		focus.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
	};
  • scrollIntoView의 옵션으로 부드러운 애니메이션 behavior: 'smooth' , ref의 제일 상단으로 이동하는 block: 'start'

3. 섹션마다 ref 속성을 추가해주고, 지정해준다.

  	{/* 플레이리스트 이름 Input Section */}
	<section ref={focusFirst}>
			...
		<button onClick={() => onMoveToFocus(focusSecond)} disabled={data.roomName === ''}>다음</button>
	</section>

	{/* 플레이리스트 카테고리 선택 Section */}
	<section ref={focusSecond}>
		...
  		<button onClick={() => onMoveToFocus(focusLast)}>다음</button>
	</section>

	{/* 플레이리스트 공개 범위 선택 Section */}
	<section ref={focusLast}>
  		...
		<button onClick={onAddRoom} disabled={data.accessAuth === ''}>생성</>
	</section>

4. 버튼 클릭 속성에 onMoveToFocus 함수로 스크롤 이동을 지정해준다.

5. 버튼 비활성화는 현재 input의 value의 상태가 빈 상태일 때 disabled 속성을 추가하여 구현하였다.

🐣 마무리

어렵지 않은 기능이지만 단순히 구현에만 집중하지 않고 사용하는 웹 API에 대해 공부하고 정리해보니 기억에 더 잘 남는 것 같다. 생성 페이지에서 남은 기능은 기능3이다!

기능3 [버튼이 아닌 그냥 스크롤로 이동을 해도 입력 화면으로 focus]를 구현하기 위해서 IntersectionObsever API를 사용하려고 한다. 다음 글에 이어서 정리해보겠다.

또 Next.js 페이지 컴포넌트에서 바로 짠 코드이기 때문에 섹션 3개가 나열되어 있어서 가독성이 아쉽다.

가독성과 유지보수를 위해서 컴포넌트화하고, 재사용성도 생각해보며 리팩토링할 예정이다.

참고자료
https://simsimjae.tistory.com/417
https://amyhyemi.tistory.com/244?category=873656
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
https://velog.io/@dosilv/ReactWeb-API-useRef-scrollIntoView

profile
Frontend-developer

0개의 댓글

관련 채용 정보