You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
tabindex에 0 또는 양수 값을 갖는 요소 및 focus가 기본적으로 가능한 form 요소들이 있다.
그렇다면 focusable과는 어떤 차이가 있을까?
tabindex에 -1을 주면 사용자가 tab키로 접근할 수는 없지만 js를 통해 focus()로 포커스를 줄 수는 있다.
위와 같이 focusable 요소는 사용자가 tab키로 접근할 수는 없지만 js를 통해 focus를 줄 수 있는 요소까지를 포함한다.
href 어트리뷰트를 갖는 a와 area 태그
button, input, select, textarea, iframe 요소
control 어트리뷰트를 갖는 video, audio 요소
summary, details 요소
tabindex, contenteditable 어트리뷰트를 갖는 경우
위에 해당하는 요소들을 모두 querySelectorAll로 찾아와서, 모달창 안에서만 키보드 네비게이션이 돌도록 작업해줄 것이다.
KeyBoard Trap 만들기
esc 키를 누르면 닫히고 모달 다이얼로그 열기 버튼에 focus를 가게 해줄 것이다.
먼저 focusable 요소인지 판별하는 유틸함수를 만든다. (왜 focusable element를 데려와서 이걸 다시 하는 것일지 야무님께 물어보기)
초점 이동 가능한 HTML 요소들을 모두 가져와 focusable인지 확인 후 배열에 넣는 유틸함수를 만들고, 이 배열의 처음과 끝에 해당하는 요소들에서 tab 또는 shift+tab을 눌렀을 때 서로에게 focus가 가도록 해준다.
ref의 current에서 자기 자손 요소들의 focusable 요소를 모두 가져와 배열로 받아온다.
firstFocusableElement와 lastFocusableElement를 배열의 0번 인덱스와 length-1번 인덱스로 받아 할당한다.
외부 요소를 자식으로 렌더링하기
vue.js에서는 slot이라는 요소 안에 외부에서 지정한 children을 끼워넣어준다. 또한 named slot을 통해 원하는 위치에 넣을 수 있도록 해준다.
리액트에서는 children을 통해 custom component의 컨텐츠 영역에 들어온 다른 요소들을 렌더링 해줄 수 있지만, 지정된 자리에 넣고 싶은 경우 compound component 패턴으로 이를 구현할 수 있다.
vue.js에는 미리 컴포넌트 안에 slot을 마련해 두었기 때문에 외부에서 children을 넣어주지 않아도 마크업 상 container slot이 남아 있는데, 리액트는 밖에서 조립하기 때문에 children이 없는 경우 마크업이 깔끔해진다
Dialog라는 컴포넌트 안에 static 멤버로 컴포넌트를 리턴하는 메서드를 만들어주자
exportclassDialogextendsReact.Component{staticHead({ className ='', ... restProps}){return<divclassName={classNames("head",className)}{...restProps}/>;}// property에 함수를 넣으면서, named function으로 만들어주면 디버깅할 때 편하다.staticMain=functionDialogMain({as: Comp='article',className, ... restProps}){return<CompclassName={classNames('content',className)}...restProps/>;}// Foot, CloseButton 등도 위와 같이 넣어줄 수 있다.}// 활용할 때는 아래와 같이 끼워넣어준다.exportfunctionApp(){return(<Dialog><Dialog.Head><h2>다이얼로그 제목<h2></Dialog.Head><Dialog.Main><p>다이얼로그 내용</p></Dialog.Main></Dialog>
)
}
로딩 접근성
로딩 중이라는 것이 시각적으로만 표현되고 텍스트로 상태가 제공되지 않으면 스크린리더로만 상황을 파악해야 하는 유저는 오류라고 생각해서 계속 새로고침을 할 수 밖에 없다.
로딩 스피너도 Portal을 통해 가장 바깥에 container을 두고 role="alert" aria-live="assertive"로 스크린리더가 읽을 수 있도록 제공해야 한다.
로딩이 시작될 때 container에 로딩시작 문구가 삽입되면 aria-live가 assertive이기 때문에 스크린리더가 다른 작업을 중단하고 바로 읽는다.
로딩을 마치면 로딩 시작 문구를 삭제하고 로딩을 마쳤다는 문구를 삽입한다.
Hook
왜 React는 함수 컴포넌트로의 패러다임 전환을 이끄는 Hook을 만들었을까?
Wrapper Hell: 고차 컴포넌트(추상레이어)로 한개씩 싸야 했던 클래스 컴포넌트의 문제. 몇 개의 중첩만 되어도 wrapper이 너무 많아진다.
Huge Component: 코드가 비대해진다.
Confusing Classes: 코드가 복잡하고 어렵다
이런 문제로 인하여 refactoring과 TDD가 어려워지기 때문에, 기존의 class component를 대체할 function component를 만들어 코드를 심플하게 만들자는 의도
근데 function component는 매번 재실행되므로 state를 가질 수 없는데...?
component 내의 상태나 ref를 추출하여 다른 component에서 재사용할 수 있게 하는 hook을 통해 가능하다!
React Hook은 캡슐화된 local stated와 logic을 외부로 꺼내 다른 컴포넌트에서도 재사용할 수 있게 해준다.
React Hook은 무엇인가
hook은 제약사항이 있는, 일반함수보다 조금 더 특별한 함수이다.
단방향 데이터 흐름을 가능케 한다.
배열에서 상태를 꺼내와 사용하게 한다.
Hook의 장점
wrapper hell 없는 clean tree를 만들어준다.
코드를 쉽고 slim하게 한다.
클래스는 최신문법이기 때문에 polyfill로 변환해줘야 했는데 function은 그럴 필요가 없기 때문에 성능최적화된 형태이다.
Hook의 단점
클래스와 함수를 구분지었던 것이 state의 유무였는데 다 function으로 쓰면 어떤 것이 stateful인지 stateless인지 명확하지 않다. (하지만 이게 크게 문제될 것은 아니다.)
useState
초기값을 넣어 호출하면 state와 이 state를 조작할 수 있는 함수를 담은 배열을 반환한다.
하나의 state를 하나씩 관리해서 관심사를 분리할 수 있다.
useEffect
componentDidMount, componentDidUpdate, componentWillUnmount의 역할을 수행한다.
첫 번째 인자로 콜백함수를 전달하고, 두 번째 인자로는 dependency Array를 전달한다.
콜백함수 몸체는 component가 mount되거나 update 될 때 실행되며 함수를 리턴하면 이를 unmount 시점에 실행한다.
dependencyArray에 특정 state를 넘기면 그 state가 변경될 때만 실행한다.