리액트@18 의 서버 컴포넌트를 사용할 수 있는 nextjs@13.4 가 정식으로 릴리즈되었음, 서버컴포넌트는 리액트 렌더링에 필요한 정보를 스트림으로 제공하는 것으로 생각하면 된다.
Next.js app dir 세팅으로 리팩터링 예시 진행할 것이다. 아래의 app/about/page.tsx는 app dir를 적용하기 전 pages/about.tsx와 같다.
Base Code
// 신호등의 신호를 가리키는 열거형과 차가 지나가도 되는지 여부를 결정하는 함수
const car = {
drive() {
console.log("차가 지나갑니다.");
},
stop() {
console.log("차가 멈춥니다.");
},
};
enum TrafficLight {
RED,
YELLOW,
GREEN,
}
// CYCLE은 신호등의 신호를 순서대로 담은 배열..
const CYCLE = [TrafficLight.RED, TrafficLight.GREEN, TrafficLight.YELLOW];
function updateCarForLight(current: TrafficLight) {
if (current === TrafficLight.RED) {
car.stop();
} else {
car.drive();
}
}
TypeScript
복사
왜 이 코드는 enum을 적용했는가? 객체를 사용해서 특정 값을 전달할 수 있게 제한하는 것도 가능한데.
위 코드를 보면 원래 책 예제 코드에는 없는 car라는 객체가 있다. 컴파일러에서 에러를 발생시키기 때문에 임시로 만들어두었다. CYCLE 배열도 사용되는 곳이 없는데 들어가있다. 왜 넣어둔 건지 궁금하지만 일단 두자.
// 1. enum의 각 값에 대한 메서드를 가진 임시 인터페이스 생성
interface TrafficLight2 {
isRed(): boolean;
isYellow(): boolean;
isGreen(): boolean;
}
// 2. 각 enum 값에 대한 클래스 만듦. 클래스에 해당하는 메서드를 제외한 인터페이스의 모든 메서드는 false를 반환
class Red implements TrafficLight2 {
isRed() {
return true;
}
isYellow() {
return false;
}
isGreen() {
return false;
}
}
class Yellow implements TrafficLight2 {
isRed() {
return false;
}
isYellow() {
return true;
}
isGreen() {
return false;
}
}
class Green implements TrafficLight2 {
isRed() {
return false;
}
isYellow() {
return false;
}
isGreen() {
return true;
}
}
TypeScript
복사
Base Code를 가지고 우리는 2개의 단계를 먼저 진행한다.
사용중인 enum 값들이 있고, 그 값들에 대해 각각 일치하는지 확인하는 메서드를 가진 인터페이스를 임시로 만들고 그 인터페이스를 구현한 클래스를 enum 값별로 만들어서 내부 메서드 중 해당 값과 일치하는 값일 때 true고 나머지는 false가 되도록 작성한다.
// 3. enum의 이름을 다른 이름으로 바꾼다. 이로 인해 컴파일러가 enum을 사용하는 모든 위치에서 오류를 발생시킨다.
enum RawTrafficLight {
RED,
YELLOW,
GREEN,
}
// 5. 열거형 값에 대한 나머지 참조를 새 클래스를 인스턴스화해서 교체한다.
const CYCLE = [new Red(), new Yellow(), new Green()];
// 4. 타입을 이전 이름에서 임시 이름으로 변경하고 일치 여부 검사를 새로운 메서드로 변경한다.
function updateCarForLight(current: TrafficLight2) {
if (current.isRed()) {
car.stop();
} else {
car.drive();
}
}
TypeScript
복사
기존의 enum TrafficLight에 Raw라는 prefix를 붙인다.
그 다음 1번에서 만든 인터페이스를 이용한다. 이전updateCarForLight의 매개변수 current의 타입은 TrafficLight2로 변경하고 메서드로 일치 비교를 대체할 수 있다! CYCLE 배열의 값들은 클래스를 인스턴스화해서 교체하게 된다.
// 신호등의 신호를 가리키는 열거형과 차가 지나가도 되는지 여부를 결정하는 함수
const car = {
drive() {
console.log("차가 지나갑니다.");
},
stop() {
console.log("차가 멈춥니다.");
},
};
// 1. enum의 각 값에 대한 메서드를 가진 임시 인터페이스 생성
interface TrafficLight {
isRed(): boolean;
isYellow(): boolean;
isGreen(): boolean;
}
// 2. 각 enum 값에 대한 클래스 만듦. 클래스에 해당하는 메서드를 제외한 인터페이스의 모든 메서드는 false를 반환
class Red implements TrafficLight {
isRed() {
return true;
}
isYellow() {
return false;
}
isGreen() {
return false;
}
}
class Yellow implements TrafficLight {
isRed() {
return false;
}
isYellow() {
return true;
}
isGreen() {
return false;
}
}
class Green implements TrafficLight {
isRed() {
return false;
}
isYellow() {
return false;
}
isGreen() {
return true;
}
}
// 3. enum의 이름을 다른 이름으로 바꾼다. 이로 인해 컴파일러가 enum을 사용하는 모든 위치에서 오류를 발생시킨다.
enum RawTrafficLight {
RED,
YELLOW,
GREEN,
}
// 5. 열거형 값에 대한 나머지 참조를 새 클래스를 인스턴스화해서 교체한다.
const CYCLE = [new Red(), new Yellow(), new Green()];
// 4. 타입을 이전 이름에서 임시 이름으로 변경하고 일치 여부 검사를 새로운 메서드로 변경한다.
function updateCarForLight(current: TrafficLight) {
if (current.isRed()) {
car.stop();
} else {
car.drive();
}
}
TypeScript
복사
이제 TrafficLight2를 TrafficLight로 변경한다. 그런데, 코드가 어마무시하게 늘어났다.
이 리팩터링 패턴은 모든 나중에 환상적인 개선을 가능하게 한다고 한다. 모든 값에 대한 메서드를 가지는 것도 스멜이므로 하나의 스멜을 다른 스멜로 대체한 것이다. 열거형 값은 밀접하게 연결되어 있으나 메서드는 하나씩 처리가 가능하다. 여기서 is로 시작하는 메서드는 일시적인 것이며, 이 장에서 일부를 제거하고 5장에서는 더 많이 제거하게 된다.