Build a traffic light where the lights switch from green to yellow to red after predetermined intervals and loop indefinitely. Each light should be lit for the following durations:
You are free to exercise your creativity to style the appearance of the traffic light.
주어진 요구사항에 맞게 간단한 신호등 컴포넌트를 구현하면 된다. 빨간 불은 4초, 노란 불은 0.5초, 초록 불은 3초를 유지하면 되고 이 과정이 무한반복 된다.
나는 총 7.5초 과정을 무한반복 해주고, 그 사이에서 현재 불 색(red | yellow | green)을 변경해주었다.
// TrafficLight.jsx
import { useState, useEffect } from "react";
export default function TrafficLight() {
const [light, setLight] = useState("red");
useEffect(() => {
setInterval(() => {
setLight("red");
setTimeout(() => {
setLight("yellow");
}, 4000);
setTimeout(() => {
setLight("green");
}, 4500);
}, 7500);
}, []);
return (
<div className="traffic_light_body">
<div className={`traffic_light_item ${light === "red" ? "red" : ""}`} />
<div
className={`traffic_light_item ${light === "yellow" ? "yellow" : ""}`}
/>
<div
className={`traffic_light_item ${light === "green" ? "green" : ""}`}
/>
</div>
);
}
body {
font-family: sans-serif;
}
.traffic_light_body {
width: 300px;
height: 100px;
background-color: black;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
}
.traffic_light_item {
width: 80px;
height: 80px;
border-radius: 80px;
}
.traffic_light_item.red {
background-color: red;
}
.traffic_light_item.yellow {
background-color: yellow;
}
.traffic_light_item.green {
background-color: green;
}
빨간 불이 4초, 노란 불이 0.5초, 초록 불이 3초 유지되면서 무한 반복되는 신호등이 구현되었다. 하지만 솔루션
을 확인한 후 아쉬운 점이 발견되었다.
먼저, 신호등에 대한 데이터 구조를 정의하였다.
const config = {
red: {
duration: 4000,
next: 'green',
},
yellow: {
duration: 500,
next: 'red',
},
green: {
duration: 3000,
next: 'yellow',
},
};
데이터 구조를 정의하게 되면 아래와 같은 장점을 얻을 수 있다.
재사용성:
config 객체는 각 신호등 상태의 지속 시간(duration)과 다음 상태(next)를 명시적으로 정의하므로, 이 구조를 통해 상태 전환 로직을 여러 신호등에 재사용할 수 있습니다. 새로운 신호등 상태나 시간 변경 시, 해당 상태만 수정하면 되기 때문에 유지보수가 쉽습니다.
확장성:
새로운 신호 상태가 추가되거나 특정 상태의 지속 시간이 변경되더라도, config만 업데이트하면 전체 시스템에 적용할 수 있습니다. 이로 인해 코드 확장 및 변경이 용이합니다.
명확한 흐름 제어:
각 상태에서 다음 상태를 지정(next)함으로써, 신호등의 흐름이 명확하게 정의됩니다. red → green → yellow → red처럼 상태 전환이 명확하게 설정되므로, 상태 전환 로직을 복잡하게 구현할 필요 없이 이 객체만 참고하여 처리할 수 있습니다.
간결성:
신호등 상태와 전환 로직이 단일 구조로 관리되기 때문에 코드가 간결해지고, 여러 곳에서 동일한 논리 흐름을 따를 수 있습니다. 코드를 분산시키지 않고 중앙에서 제어할 수 있어 직관적입니다.
유연성:
각 상태마다 duration과 next를 개별적으로 설정할 수 있기 때문에, 필요에 따라 상태의 지속 시간이나 전환 순서를 쉽게 커스터마이즈할 수 있습니다. 이를 통해 다양한 조건에 맞춘 신호등 시스템을 빠르게 만들 수 있습니다.
import { useEffect, useState } from 'react';
function Light({ backgroundColor }) {
return (
<div
aria-hidden={true}
className="traffic-light"
style={{ backgroundColor }}
/>
);
}
export default function TrafficLight({
initialColor = 'green',
config,
layout = 'vertical',
}) {
const [currentColor, setCurrentColor] =
useState(initialColor);
useEffect(() => {
const { duration, next } = config[currentColor];
const timerId = setTimeout(() => {
setCurrentColor(next);
}, duration);
return () => {
clearTimeout(timerId);
};
}, [currentColor]);
return (
<div
aria-live="polite"
aria-label={`Current light: ${currentColor}`}
className={[
'traffic-light-container',
layout === 'vertical' &&
'traffic-light-container--vertical',
]
.filter((cls) => !!cls)
.join(' ')}>
{Object.keys(config).map((color) => (
<Light
key={color}
backgroundColor={
color === currentColor
? config[color].backgroundColor
: undefined
}
/>
))}
</div>
);
}
그리고, 메모리 누수
를 신경썼다. 먼저 타이머를 설정하고,
const timerId = setTimeout(() => {
setCurrentColor(next);
}, duration);
컴포넌트가 언마운트 되거나 업데이트 될 때 타이머를 정리하였다. 불필요한 타이머가 쌓이는 것을 방지하였다.
return () => {
clearTimeout(timerId);
};
<div aria-live="polite" >
역할: 이 속성은 화면 판독기(screen reader)가 신호등의 상태 변화를 사용자에게 알리도록 합니다.
polite: aria-live="polite"는 상태 변경을 즉시 읽지 않고, 사용자가 현재 작업을 끝낸 후에 신호등 상태를 알립니다. 급하지 않은 정보에 적합한 방식입니다.
이점: 시각장애인 사용자들이 화면 판독기를 통해 신호등 상태 변경을 확인할 수 있게 해줍니다.
aria-label={`Current light: ${currentColor}`}
역할: 현재 신호등의 상태(빨간색, 노란색, 초록색)를 사용자에게 알려주는 역할을 합니다.
이점: 화면 판독기는 이 aria-label을 통해 현재 신호등이 어떤 상태인지 정확하게 읽어주므로, 사용자는 신호등의 시각적 상태를 알지 못해도 정보를 확인할 수 있습니다.
<div aria-hidden={true} />
역할: 신호등의 시각적 표현 부분인 Light 컴포넌트에 aria-hidden={true}를 적용하여, 화면 판독기가 불필요하게 신호등의 시각적 요소를 읽지 않도록 방지합니다.
이점: 화면 판독기가 중복된 정보를 읽지 않게 하여, 사용자 경험을 향상시킵니다. 이미 aria-label로 상태를 전달했기 때문에 시각적 요소 자체는 굳이 읽을 필요가 없습니다.
이로 인해 시각장애인 사용자들도 신호등 상태를 명확하게 인식할 수 있고, 중복된 정보가 제공되지 않으므로 깔끔한 접근성 경험을 제공합니다.
간단한 신호등 컴포넌트를 만들면서 아래와 같은 점이 아쉬웠다.
요구사항 미충족
출제자가 의도한 요구사항은 빨간 불 -> 노란 불 -> 초록 불 -> 노란 불 -> 빨간 불
이 무한반복되는 것을 의도했는데 (일반적인 신호 체계), 나는 빨간 불 -> 노란 불 -> 초록 불
이 과정이 반복됨으로써 초록 불 이후 노란 불이 들어오지 않는다. 요구사항을 완벽하게 파악하지 못했던 점이 아쉬웠다.
데이터 구조 정의
데이터 구조 정의하는 습관을 길러 명확하게 컨텍스트를 파악할 수 있게 하고, 코드 가독성을 높혀야겠다.
메모리 누수 문제
타이머 함수 같은 경우에는 클린업 함수에서 정리하는 습관을 길러야겠다.
웹 접근성
시멘틱 태그 말고도, 웹 접근성을 지킬 수 있는 요소들을 확인했다. 신호등 같은 경우에는 더더욱 중요한 aria-label
같은 요소를 작성하여 웹 접근성을 지키자.
여기에서 코드를 직접 작성해볼 수 있습니다.
(greatfrontend)