개발 이모저모

서버 데이터와 컴포넌트의 분리: 유지보수성을 높이는 방법

눙엉 2024. 6. 16. 12:23

 

서버데이터를 사용하여 렌더링 하는 컴포넌트의 유지보수에 대한 이야기를 해보려 합니다.

저의 주관적인 생각이며, 각자 생각이 다를 수 있기 때문에 이렇게 생각할 수도 있구나라고 봐주시면 감사하겠습니다 :)

 

개발 중 서버데이터의 변경으로 인해 컴포넌트를 수정하다 문득 "왜..? 서버데이터가 변경되었는데 컴포넌트까지 수정해야 되는 걸까?"라는 생각이 들었습니다.

 

결론을 먼저 말하자면 컴포넌트를 제작할 때 필요한 데이터 타입을 따로 작성하는 것이 단일 책임 원칙에 조금 더 가까운 방법인 것 같다입니다. 당연한 소리 같이 들리겠지만 제가 컴포넌트를 제작하는 경우에는 당연한 방법으로 하지 않고 있었다는 것을 깨달은 지 얼마 되지 않았던 것 같습니다. 아래의 예시로 이어서 설명하겠습니다.

interface Before {
  name: string;
  age: number;
  school: string;
  friendNumber: number;
  howToGetToSchool: string;
}

 

Before 타입을 가진 데이터를 서버에서 반환받아 렌더링 하는 컴포넌트를 제작해야 되는 경우입니다.

필자의 경우 서버 데이터를 그대로 컴포넌트가 전달받아 사용하고 있으며, 해당 컴포넌트 props의 data 타입으로 사용하고 있었습니다.

interface BeforeStudent {
  name: string;
  age: number;
  school: string;
  friendNumber: string;
  howToGetToSchool: string;
}

const Page = () => {
  // 서버에서 데이터를 받아오는 함수
  const studentData:BeforeStudent = getStudent();

  return <StudentComponent data={studentData} />
}
interface StudentComponentProps {
  data: BeforeStudent;
}

const StudentComponent = ({data}: StudentComponentProps) => {
  return (
    <div>
      <div>{`이름은 ${data.name} 입니다.`}</div>
      <div>{`나이는 ${data.age} 입니다.`}</div>
      <div>{`친한 친구의 수는 ${data.friendNumber} 입니다.`}</div>
    </div>
  )
}

export default StudentComponent

 

BeforeStudent타입의 모든 데이터를 사용하는 컴포넌트지만 코드상으로는 간략히 작성했습니다.

만약 서버에서 반환해 주는 데이터의 형식이 변경된다면 어떻게 될까요? 모종의 이유로 BeforeStudent의 타입이 AfterStudent로 변경되었다고 해보겠습니다.

interface AfterStudent {
  person: {
    name: string;
    age: number;
    school: string;
  },
  friendNumber: number;
  howToGetToSchool: string;
}

 

BeforeStudent의 타입을 사용하고 있는 StudentComponent는 에러를 발생시킬 것입니다. 필자는 에러 해결을 위한 방법으로 아래와 같이 해결하곤 했습니다.

interface StudentComponentProps {
  data: AfterStudent;
}

const StudentComponent = ({data}: StudentComponentProps) => {
  return (
    <div>
      <div>{`이름은 ${data.person.name} 입니다.`}</div>
      <div>{`나이는 ${data.person.age} 입니다.`}</div>
      <div>{`친한 친구의 수는 ${data.friendNumber} 입니다.`}</div>
    </div>
  )
}

export default StudentComponent

 

물론 해당 코드로 수정해도 전혀 문제가 없습니다. 하지만 StudentComponent의 기능이 변경되어 해당 컴포넌트의 코드를 수정한 것인가요? 전혀 아닙니다. 단지 서버의 반환 형식이 변경되었고 해당 데이터 형식을 그대로 사용하도록 컴포넌트를 제작했기 때문에 컴포넌트의 내부 코드를 수정하게 된 것입니다.

위에서도 언급했듯이 이러한 수정 방법이 잘못되었다고 느꼈습니다. 컴포넌트의 필요한 데이터 타입과 서버의 반환 데이터 타입은 별개의 데이터 타입으로 작성되어야 한다고 생각합니다.

interface StudentComponentProps {
  data: {
    name: string;
    age: number;
    friendNumber: number;
  };
}

const StudentComponent = ({ data }: StudentComponentProps) => {
  return (
    <div>
    <div>{`이름은 ${data.name} 입니다.`}</div>
    <div>{`나이는 ${data.age} 입니다.`}</div>
    <div>{`친한 친구의 수는 ${data.friendNumber} 입니다.`}</div>
    </div>
  )
}

export default StudentComponent

 

StudentComponent의 props 타입을 서버데이터와 관련 없이 컴포넌트 자체만의 타입으로 재정의 해보았습니다. 위와 같이 컴포넌트를 작성하게 된다면 서버에서 반환해 주는 데이터가 변경되더라도 컴포넌트의 코드는 수정할 일이 없습니다.

interface AfterStudent {
  person: {
    name: string;
    age: number;
    school: string;
  },
  friendNumber: number;
  howToGetToSchool: string;
}


const Page = () => {
  // 서버에서 데이터를 받아오는 함수
  const studentData:AfterStudent = getStudent();
	
  const data = {
    name: studentData.name, 
    age:studentData.age, 
    friendNumber: studentData.friendNumber
  }

  return <StudentComponent data={data} />
}

 

물론 위의 코드처럼 컴포넌트를 사용하는 곳에서의 코드 수정이 필요한 건 맞습니다. 수정 전과 후 모두 코드를 수정해야 되지만 처음의 코드와는 다르게 StudentComponent의 코드 수정은 불필요해졌습니다. 단지 StudentComponent에 props로 전달하게 되는 데이터가 변경되었으니 해당 data를 수정할 뿐입니다. 결과적으로 StudentComponent에서 사용되는 데이터 형식과 서버의 데이터 반환 타입의 연결점이 사라졌습니다. StudentComponent는 단지 props로 전달받은 데이터를 렌더링 해줄 뿐입니다.

 

컴포넌트가 항상 같은 props를 전달받는 원칙을 따르면 외부 상황의 변화에도 불구하고 컴포넌트 내부의 코드를 수정할 필요가 없어집니다. 이는 컴포넌트 유지 보수성을 크게 향상할 수 있고, "단일 책임 원칙"을 더욱 따르게 되었다고 생각됩니다. 컴포넌트는 특정 역할(데이터를 표시하는 것)에 집중하게 되었기 때문입니다.