-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[항공사 웹사이트의 컴포넌트 접근성 높이기 - 1단계] 앨버(송상민) 미션 제출합니다. #50
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
우선 리액트 코드에 대해 말씀드리고 싶은게 있어서 리뷰 남깁니다 확인해주세욥 !!!!!!!!!!!!!
src/App.tsx
Outdated
const [passengerAmountStatus, setPassengerAmountStatus] = useState(''); | ||
|
||
const handleIncreasePassengerAmount = () => { | ||
setPassengerAmount((prev) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setState에 들어가는 콜백에 대해서 이야기 해볼까욥?!
setState에 들어가는 reducer
는 순수한 함수여야 한다고 생각합니다. 오롯이 이전 상태에 의존하여 다음 상태를 만들어내는, 외부 상태를 참조하거나, 수정하지 않는
순수한 함수여야 한다고 생각해요.
이유는 다음과 같습니다.
-
만들어지는 다음 상태를 믿을 수 없다.
-
외부 상태 업데이트 부분( =
setPassengerAmountStatus("감소")
)이 정상 동작할 지 믿을 수 없다.
리액트는 객체의 불변성을 강조하고, 순수한 함수 형태로 동작 시키는 것을 좋아합니다. 이 노력들이 존재하는 이유는 그것들이 전체적으로 프로그램을 예측 가능하게 해주기 때문이라고 생각하는데요. 이러한 리액트의 의도와 앨버의 코드는 반대되는 양상인 것 같습니다. setState의 콜백함수는 이전 상태를 통해 다음 상태를 만드는 일만 해야하는데, 지금 코드로는 그렇지 않으니깐요. 앨버가 트리거 한 외부 상태 업데이트
작업 ( = setPassengerAmountStatus("감소");
)이 현재 상태의 업데이트에 영향을 미칠지도 모르잖아요? 그래서 저는 다음과 같이 작성하는 것을 제안드립니다!!
(앨버 코드에 따른다면, 배치 처리 대상에서 setPassengerAmountStatus("감소");
는 제외되지 않을까 조심스레 예측해봅니다..)
const handleDecreasePassengerAmount = () => {
if (passengerAmount <= 1) {
alert("최소 승객은 1명입니다");
return;
}
setPassengerAmount((prev) => prev - 1);
setPassengerAmountStatus("감소");
};
- Flux Architecture
- React의 useReducer 훅 (useState 훅은 useReducer 훅의 기본형인 것으로 알고 있습니다. 사실 저 훅의 인자로 들어가는 콜백함수는 useReducer의 reducer function입니다.)
- Redux의 reducer는 왜 순수해야하는가
이거 세 개 같이 공부해보고 만나면 또 토론해요 !!!!!!!!!!!! 이거 최근에 다른 분한테도 질문을 받았었는데, 그 때도 앨버 답글 달듯이 모호하게만 말씀드린 것 같아요. 같이 공부해보시죠!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그리고 상태 업데이트의 배치 처리 항목에 대해서도 같이 봐보져 저도 까먹었네요 ㅋㅋㅋ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After the event handler completes, React will trigger a re-render. During the re-render, React will process the queue. Updater functions run during rendering, so updater functions must be pure and only return the result. Don’t try to set state from inside of them or run other side effects. In Strict Mode, React will run each updater function twice (but discard the second result) to help you find mistakes.
https://beta.reactjs.org/learn/queueing-a-series-of-state-updates#react-batches-state-updates
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
준찌...답글 보자마자 너무 좋았습니다 😀
이런 토론 마구마구 하면 좋을 것 같아요!!
일단, setState의 콜백함수 내부에서, 다른 상태의 setState를 하도록 작성한 코드는 만약 제가 꼼꼼히 봤다면 바꿨을 코드인데 이번에는 꼼꼼하지 못했습니다.
근데, 준찌가 남겨준 피드백을 보고 왜 setState의 콜백함수 내에서 다른 setState를 하면 안될까
라는 생각은 해본 적이 없구나라는 것을 깨달았어요!! 준찌가 남겨준 댓글 보고 어느 정도 감을 잡을 수 있었습니다 감사해요
type SetStateAction<S> = S | ((prevState: S) => S);
setStateAction의 타입을 봐도, 순수함수를 요구하는 느낌인 것 같아서 공유드립니다ㅎㅎ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수정하였습니다. f4d79c4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
우선 궁금한 것들만 남겨보았어요!!! Approve합니다 LGTM
gap: 1rem; | ||
} | ||
|
||
.sr-only { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 스크린리더 전용인가요? 의도 및 스타일 설명 조금 남겨주실수있을까요?!?!??🥹
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정확하게, 각자의 css 속성이 어떻게 화면에서 숨겨짐과 동시에 스크린 리더가 읽을 수 있도록 하는 지 세세한 사항까지는 모르겠더라고요🙄
sr.only 클래스내 css 속성중에 생소한 것들에 대해서 주석을 남겨보았습니다
.sr-only {
border: 0 !important; // 요소의 테두리를 0으로 없애줍니다. (테두리가 있는 요소가 있는 경우)
clip: rect(1px, 1px, 1px, 1px) !important; // clip(자르다)라는 단어의 의미처럼 top-left(왼쪽 위)를 기준으로, 픽셀 값들을 토대로 잘라줍니다.
-webkit-clip-path: inset(50%) !important;
clip-path: inset(50%) !important; // clip과 비슷한 역할을 합니다.
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
width: 1px !important;
white-space: nowrap !important;
}
sr-only
아래 표를 참고하면, sr-only는 화면에는 보이지 않지만, 스크린 리더는 접근가능한 속성인 것을 알 수 있습니다.
https://yceffort.kr/2021/03/hiding-contents-with-html-and-css
const [passengerAmount, setPassengerAmount] = useState(1); | ||
const [passengerAmountStatus, setPassengerAmountStatus] = useState(''); | ||
|
||
const handleIncreasePassengerAmount = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드가 훨씬 간결해지고 리액트의 의도와 맞닿아졌네요!!! 정답은 없지만, 리액트를 따라가고 있는 이상 그들의 방식에 맞춰봅시다!!
@@ -0,0 +1,4 @@ | |||
declare module '*.png' { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
엠비언트 모듈 내부의 모습이 특이하네요 이게 뭐죠?
@@ -0,0 +1,3 @@ | |||
# https://www.robotstxt.org/robotstxt.html | |||
User-agent: * |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Http header에도 보이는 이 녀석 도대체 뭘까요?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
한 번도 무엇일까 찾아본 적이 없었는데 덕분에 찾아보게 되었습니다.
MDN에는 아래와 같이 정의 되어 있네요
The User-Agent request header is a characteristic string that lets servers and network peers identify the application, operating system, vendor, and/or version of the requesting user agent
쉽게 말하면, 서버에서 요청을 보낸 놈이 누구냐~~ 를 식별하기 위한 헤더라고 보면 될 것 같습니다.
관련해서, 찾아보니 현재 제 자신의 user-agent 정보를 파싱해서 보여주는 재밌는 사이트가 있어서 저의 user-agent를 공개합니다!
value={passengerAmount} | ||
/> | ||
<button onClick={handleIncreasePassengerAmount}> | ||
<img |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 이미지를 사용하시고 대체 텍스트를 추가하셨군요!! 일반 엘리먼트일때 방법도 알고 계신다면 복습 겸 남겨주세욥!! 키키
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일반 엘리먼트
일 때가 혹시, html tag인 경우를 말씀하시는 걸까요??
다른 방법이 또 있는 건가요?!?!?ㅎㅎ힌트 주시면 감사하겠습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아 혹시 aria-label 말씀하시는 걸까요???
<button>
<div aria-label="승객 늘리기 버튼">
</button>
이렇게 사용하게 되면 img의 alt 태그와 동일하게 스크린 리더가 읽어줄 수 있습니다!~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞아욥!!!! 정확히 아시는군요!!!
안녕하세요 준찌 앨버입니다!!😀
부족한 부분들 피드백해주세요~~ㅎㅎ
🔥 결과
직접 구현한 페이지를 작동시켜볼 수 있도록 접근 가능한 페이지 링크를 공유해주세요.
링크
✅ 요구사항 목록
1 Spin Button
2 Carousel
🧐 공유
aria-live, aria-atomic
스크린 리더는 동적으로 변하는 요소들을 읽지 못합니다. 그래서, aria-live을 통해서 동적으로 변하는 것들을 읽어주도록 영역을 설정해줄 수 있습니다.
아래와 같은 상황이라면, dynamicContent가 동적으로 바뀌게 되면, 스크린 리더가 해당 내용을 읽어줍니다.
하지만, 아래의 경우는, 승객은 변하지 않았기 떄문에, 읽지 않습니다. 승객도 포함해서 읽어주게 하기 위해서는
aria-atomic
속성을 사용해주면 됩니다.