Skip to content
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

Merged
merged 9 commits into from
Oct 11, 2022

Conversation

al-bur
Copy link

@al-bur al-bur commented Oct 6, 2022

안녕하세요 준찌 앨버입니다!!😀

wnsWlsms dbfud

부족한 부분들 피드백해주세요~~ㅎㅎ

🔥 결과

직접 구현한 페이지를 작동시켜볼 수 있도록 접근 가능한 페이지 링크를 공유해주세요.

링크

✅ 요구사항 목록

1 Spin Button

  • 최대 인원수는 3명까지만 가능하게 구현합니다.
  • 실제 스크린 리더는 아래와 같이 읽을 수 있어야 합니다. 단, 스크린 리더기 마다 읽는 방법이 다를 수 있으니 참고만 해주세요. 중요한건 스크린 리더기를 활용하여 동작을 할 수 있어야 합니다.
  • 리팩터링 과정에서 불필요하거나 웹 표준에 어긋나는 마크업은 없는지 확인합니다.
성인 탑승자 한명 줄이기 버튼
성인 1 텍스트 숫자만 수정
성인 탑승자 한명 늘리기 버튼
성인 승객 추가 2
성인 탑승자 한명 늘리기 버튼
성인 승객 추가 3

2 Carousel

  • 총 8개의 carousel 중 2개의 목록을 기본으로 보여준다.
  • 실제 스크린 리더는 아래와 같이 읽을 수 있어야 합니다. 단, 스크린 리더기 마다 읽는 방법이 다를 수 있으니 참고만 해주세요. 중요한건 스크린 리더기를 활용하여 동작을 할 수 있어야 합니다.
  • 리팩터링 과정에서 불필요하거나 웹 표준에 어긋나는 마크업은 없는지 확인합니다.
1. 지금 떠나기 좋은 여행
2. 목록 8개 항목 포함 서울/인천 로스앤젤레스 일반석 왕복 1,481,800 대한민국 원 링크 목록 항목
3. 다음 버튼 (사용 중지)
4. 이전 버튼 (사용 중지)

🧐 공유

aria-live, aria-atomic

스크린 리더는 동적으로 변하는 요소들을 읽지 못합니다. 그래서, aria-live을 통해서 동적으로 변하는 것들을 읽어주도록 영역을 설정해줄 수 있습니다.

아래와 같은 상황이라면, dynamicContent가 동적으로 바뀌게 되면, 스크린 리더가 해당 내용을 읽어줍니다.

// react를 통해서 개발한 경우
<div aria-live="polite">{dynamicContent}</div>

하지만, 아래의 경우는, 승객은 변하지 않았기 떄문에, 읽지 않습니다. 승객도 포함해서 읽어주게 하기 위해서는 aria-atomic 속성을 사용해주면 됩니다.

// react를 통해서 개발한 경우
<div aria-live="polite" aria-atomic="true">승객 {dynamicContent}</div>

@al-bur al-bur requested a review from juunzzi October 6, 2022 07:10
Copy link

@juunzzi juunzzi left a 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) => {
Copy link

@juunzzi juunzzi Oct 6, 2022

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는 왜 순수해야하는가

이거 세 개 같이 공부해보고 만나면 또 토론해요 !!!!!!!!!!!! 이거 최근에 다른 분한테도 질문을 받았었는데, 그 때도 앨버 답글 달듯이 모호하게만 말씀드린 것 같아요. 같이 공부해보시죠!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그리고 상태 업데이트의 배치 처리 항목에 대해서도 같이 봐보져 저도 까먹었네요 ㅋㅋㅋ

Copy link

@juunzzi juunzzi Oct 6, 2022

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

Copy link
Author

@al-bur al-bur Oct 6, 2022

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의 타입을 봐도, 순수함수를 요구하는 느낌인 것 같아서 공유드립니다ㅎㅎ

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정하였습니다. f4d79c4

Copy link

@juunzzi juunzzi left a 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 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 스크린리더 전용인가요? 의도 및 스타일 설명 조금 남겨주실수있을까요?!?!??🥹

Copy link
Author

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는 화면에는 보이지 않지만, 스크린 리더는 접근가능한 속성인 것을 알 수 있습니다.

image
https://yceffort.kr/2021/03/hiding-contents-with-html-and-css

const [passengerAmount, setPassengerAmount] = useState(1);
const [passengerAmountStatus, setPassengerAmountStatus] = useState('');

const handleIncreasePassengerAmount = () => {
Copy link

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' {
Copy link

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: *
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Http header에도 보이는 이 녀석 도대체 뭘까요?!

Copy link
Author

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를 공개합니다!

https://www.useragentstring.com/
image

value={passengerAmount}
/>
<button onClick={handleIncreasePassengerAmount}>
<img
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 이미지를 사용하시고 대체 텍스트를 추가하셨군요!! 일반 엘리먼트일때 방법도 알고 계신다면 복습 겸 남겨주세욥!! 키키

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일반 엘리먼트일 때가 혹시, html tag인 경우를 말씀하시는 걸까요??

다른 방법이 또 있는 건가요?!?!?ㅎㅎ힌트 주시면 감사하겠습니다

Copy link
Author

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 태그와 동일하게 스크린 리더가 읽어줄 수 있습니다!~

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아욥!!!! 정확히 아시는군요!!!

@juunzzi juunzzi merged commit 8cecbe3 into woowacourse:al-bur Oct 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants