문제 설명
과일 장수가 사과 상자를 포장하고 있습니다. 사과는 상태에 따라 1점부터 k점까지의 점수로 분류하며, k점이 최상품의 사과이고 1점이 최하품의 사과입니다. 사과 한 상자의 가격은 다음과 같이 결정됩니다.

한 상자에 사과를 m개씩 담아 포장합니다.
상자에 담긴 사과 중 가장 낮은 점수가 p (1 ≤ p ≤ k)점인 경우, 사과 한 상자의 가격은 p * m 입니다.
과일 장수가 가능한 많은 사과를 팔았을 때, 얻을 수 있는 최대 이익을 계산하고자 합니다.(사과는 상자 단위로만 판매하며, 남는 사과는 버립니다)

예를 들어, k = 3, m = 4, 사과 7개의 점수가 [1, 2, 3, 1, 2, 3, 1]이라면, 다음과 같이 [2, 3, 2, 3]으로 구성된 사과 상자 1개를 만들어 판매하여 최대 이익을 얻을 수 있습니다.

(최저 사과 점수) x (한 상자에 담긴 사과 개수) x (상자의 개수) = 2 x 4 x 1 = 8
사과의 최대 점수 k, 한 상자에 들어가는 사과의 수 m, 사과들의 점수 score가 주어졌을 때, 과일 장수가 얻을 수 있는 최대 이익을 return하는 solution 함수를 완성해주세요.

제한사항
3 ≤ k ≤ 9
3 ≤ m ≤ 10
7 ≤ score의 길이 ≤ 1,000,000
1 ≤ score[i] ≤ k
이익이 발생하지 않는 경우에는 0을 return 해주세요.

 


 

 

function solution(k, m, score) {
    var answer = 0;
    let arr = score.sort((a, b) => b - a); // 내림차순 정렬
    let box = [];
   
    arr.forEach((item,i) =>{
        box.push(arr[i])  
        
        // 박스에 사과가 m개 담겼다면 점수계산
        if(box.length === m){
            answer += Math.min(...box) * box.length;
            box=[];
        }
    })
    
    return answer;
}

중간에 발생한 문제점

처음에는 splice로 꺼내는 작업을 했더니 forEach가 멈춰버리는 일이 생겼습니다.
알아보니 forEach는 처음에 시작할때 배열의 길이와 요소를 고정된 상태로 순회한다는걸 몰랐습니다.

 

 


혹시 문제가 남은 사과까지 계산한다면..?

 

문제에서는 남은 사과는 계산하지 않습니다.

 

혹시라도 남은 사과를 계산을 했었다면

 

for문이나 while문으로짜고 splice로 잘라서 넣고 남은 arr.length가 m보다 작다면 arr의 요소를 더해서 계산하면 될 듯 싶습니다.

 

 

 

function solution(k, m, score) {
    let answer = 0;
    let arr = score.sort((a, b) => b - a);  

    // 상자 단위로 처리 (m개씩 꺼내서 상자 구성)
    while (arr.length >= m) {
        let box = arr.splice(0, m);  // m개씩 꺼내서 상자에 담기
        answer += Math.min(...box) * m;  // 최소 점수 * m (상자의 가격)
    }

    // 남은 사과가 있으면 마지막 상자에 대해 처리
    if (arr.length > 0) {
        let box = arr.splice(0, arr.length);  // 남은 사과들
        answer += Math.min(...box) * arr.length;  // 남은 사과들 가격 계산
    }

    return answer;
}

'개발공부 > 알고리즘' 카테고리의 다른 글

[Algorithm] 2차원 배열 최대합 구하기  (0) 2023.04.30
[Algorithm] 점수계산하기  (0) 2023.04.27
[Algorithm] 가위바위보  (0) 2023.04.27
[Algotithm] 큰 수 출력하기  (0) 2023.04.26
[Algorithm] 보이는 학생  (0) 2023.04.26

정의 : 고수준 모듈이 저수준 모듈에 의존해서는 안된다. 둘 다 추상화된 인터페이스에 의존해야한다.

예시 : 리엑트에서 특정 API 서비스에 직접 의존하지 않도록, 추상화된 서비스에 의존하게 설계할 수 있습니다.

 

DIP 위반 예시

function UserProfile() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch('/api/user/1')
      .then((res) => res.json())
      .then((data) => setUser(data));
  }, []);

  return (
    <div>
      <h1>{user?.name}</h1>
      <p>{user?.email}</p>
    </div>
  );
}

UserProfile 컴포넌트는 특정 API 호출방식에 의존하고 있습니다.

만약 API가 바뀐다면 이 컴포넌트도 수정을 해야합니다.

이는 DIP를 위반한 사례로 상위 컴포넌트(UserProfile)가 하위 구현(API fetch)에 의존하고 있기 때문입니다.

 


DIP 적용

// UserProfile은 오로지 UI를 렌더링하는 역할만 담당
function UserProfile({ user }) {
  return (
    <div>
      <h1>{user?.name}</h1>
      <p>{user?.email}</p>
    </div>
  );
}

UserProfile 컴포넌트는 

 

function App() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const fetchUserFromAPI = async () => {
      const response = await fetch('/api/user/1');
      const data = await response.json();
      setUser(data);
    };

    fetchUserFromAPI();
  }, []);

  return <UserProfile user={user} />;
}

 

UserProfile은 더이상 구체적인 데이터 처리나 API호출 방식에 의존하지않고 상위 컴포넌트가 제공하는 데이터(Props)를 받아 UI만 랜더링하게 됩니다. (이제보니 이렇게 만들면 ISP도 적용되는 것 같습니다)

 

 

정의 : 특정 클라이언트에 맞는 인터페이스만 구현해야 한다. 즉, 불필요한 메서드를 포함한 큰 인터페이스 대신, 작은 인터페이스 여러개로 분리해야 한다.

리엑트 기준으로 본다면 불필요한 Props를 넘기지 않고 필요한 Props를 정의하는 것으로 보면 될 것 같습니다.

 

예시 : 리액트 컴포넌트에서 필요 없는 props를 받지 않도록 필요한 props만 정의하기

 

ISP 위반 예시

하나의 컴포넌트에서 모든 기능을 처리하기 위해, ViewUser 와 EditUser 모드를 모두 저리하는 코드를  작

 

function UserInfo({ user, onEdit, onSave }) {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
      <button onClick={onEdit}>Edit</button>
      <button onClick={onSave}>Save</button>
    </div>
  );
}

 

ISP 적용 예시

// 보기 전용 컴포넌트
function UserInfo({ user, onEdit }) {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
      <button onClick={onEdit}>Edit</button>
    </div>
  );
}

// 수정 전용 컴포넌트
function EditUser({ user, onSave }) {
  const [name, setName] = useState(user.name);
  const [email, setEmail] = useState(user.email);

  return (
    <div>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <input value={email} onChange={(e) => setEmail(e.target.value)} />
      <button onClick={() => onSave({ name, email })}>Save</button>
    </div>
  );
}

 

이렇게 만들면 UserInfo는 보기기능만 EditUser는 수정기능만 담당하게 되며 필요한 Props만 받게 됩니다.

 

ISP 적용의 장점

  • 책임 분리: UserInfo 와 EditUser는 각자 자신이 해야 할 일만 담당해서 책임이 명확해집니다.
  • 불필요한 의존성 제거: UserInfo 는 onSave 같은 불필요한 props를 받지 않고, EditUser는 onEdit 같은 props가 필요 없음.
  • 확장성: 나중에 새로운 역할이나 모드가 추가될 경우, 기존 컴포넌트를 건드리지 않고 새로운 컴포넌트를 추가할 수 있어.

정의 : 소프트웨어는 확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 한다. 즉, 기능을 확장할 때 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있어야 한다.

 

즉, 기존 기능의 변경없이 기능을 확장시킬수 있게 만들자가 OCP의 목적입니다.

 

나쁜예시부터..

const menuList = ["메일", "카페", "블로그", "쇼핑", "뉴스"];

<nav>
  <Menu name={menuList[0]} />
  <Menu name={menuList[1]} />
  <Menu name={menuList[2]} />
  <Menu name={menuList[3]} />
  <Menu name={menuList[4]} />
</nav>

menuList가 추가될때 기존 코드에서 Menu라는 컴포넌트를 추가적으로 작성해야합니다..

 

네.. 정말 말도안되는 코드입니다. 설마 이런사람이 있을까 싶지만.. 예시일 뿐입니다.

 

const menuList = ["메일", "카페", "블로그", "쇼핑", "뉴스"];

<nav>
  {menuList.map((menuName) => {
    return <Menu name={menuName} />;
  })}
</nav>

 

위와 같이 작성시 menuList에  값이 추가되더라도 밑의 로직에서 수정되는 부분은 없습니다.

 


컴포넌트를 설계할때 변화에 대처하기 편한 구조를 가지는것이 중요하다! 정도로만 생각하면 될 것 같습니다.

 

예시가 좀 구리고.. 너무 극단적일 순 있지만 이런 내용이다 정도로 이해해 주시면 될 것 같습니다.

 

  • S: 단일 책임 원칙 (Single Responsibility Principle, SRP)
    • 정의: 한 클래스는 오직 하나의 책임만 가져야 한다.
    • 설명: 하나의 클래스나 모듈은 단 하나의 역할을 수행하도록 만들어져야 하며, 그 클래스나 모듈에 변경이 발생할 이유는 오직 하나뿐이어야 한다는 원칙입니다

SRP의 책임은 단순히 동작을 기준으로 컴포넌트를 쪼개는 것과는 조금 다릅니다. SRP의 핵심은 단일 책임을 가진다고 해도, 그 책임이 반드시 하나의 동작만으로 제한되지는 않습니다.

오히려 SRP는 컴포넌트가 명확한 책무를 갖도록 하는 것이 중요합니다. 이 책무는 특정한 기능이나 역할을 담당하는 것으로, 이를 통해 컴포넌트의 역할이 명확히 구분되고 유지보수성이나 확장성이 향상됩니다.

SOLID원칙을 들어가기전에.. OOP의 기본 개념다형성 캡슐화 합성 등에 대해 정리를 해보고자 합니다.
OOP의 개념을 프론트앤드의 입장에서 정리하려고 하다보니 생각보다 예시가 적절하지 않다라는 느낌이 들기도합니다..

1. 캡슐화 (Encapsulation)

  • 정의: 객체의 내부 상태(데이터)와 이를 다루는 메서드(기능)를 하나의 단위로 묶는 것을 의미하며, 외부에서는 객체 내부에 직접 접근하지 못하도록 숨기는 것을 말합니다.
  • 장점: 캡슐화를 통해 외부에서 객체의 내부 구조를 알 필요 없이 메서드를 통해서만 데이터를 조작하게 되어, 코드의 복잡성을 줄이고 유지보수를 용이하게 합니다.
  • 예시 (React):
    const MyComponent = () => {
      const [count, setCount] = useState(0);
    
    const increment = () => {
      setCount(prevCount => prevCount + 1); 
    };
    
      return <button onClick={increment}>{count}</button>;
    };
    count는 컴포넌트 내부에 캡슐화된 상태 정보로 볼 수 있으며, 외부에서는 이를 직접 수정할 수 없습니다.
    setCount나 setCount 가 포함된 함수를 통해서 상태를 업데이트 할 수 있습니다.
    즉 count는 MyComponent 내부에 캡슐화 되어 있으며, 외부에서는 직접 수정할 수 없고 incrament 함수를 통해 조작할 수 있습니다.

 

 

 

2. 합성 (Composition)

  • 정의: 여러 개의 객체를 하나로 묶어 더 큰 복합 객체를 만드는 방식으로, 객체 간 상속보다는 상호 협력을 통해 재사용성을 높이는 디자인 패턴입니다.
  • 장점: 합성을 사용하면 객체 간 결합이 느슨해지고, 유연한 구조를 만들 수 있어 유지보수성과 확장성이 향상된다. 상속의 문제점을 피하고, 더 많은 재사용성을 제공할 수 있습니다.
  • 예시 (React):
    const PageLayout = ({ children }) => (
      <div>
        <Header />
        <main>{children}</main>
        <Footer />
      </div>
    );
    
    const MyPage = () => (
      <PageLayout>
        <section>My Content</section>
      </PageLayout>
    );

    리액트에서는 합성을 통해 컴포넌트들을 조합하여 다양한 컴포넌트를 만들 수 있습니다.

3. 다형성 (Polymorphism)

  • 정의: 여러 클래스가 같은 인터페이스나 메서드 명을 공유하면서 서로 다른 방식으로 동작하는 특성을 의미합니다. 이는 객체 지향의 중요한 원칙으로, 하나의 타입을 여러 방식으로 사용할 수 있게 합니다.
    리엑트에서의 다형성은 props를 통해 동일한 컴포넌트가 다른 props를 받으면 다르게 동작하게 만들어서 
    다형성을 적용할 수 있습니다. 
  • 장점: 다형성을 사용하면 코드의 유연성과 확장성이 높아지며, 인터페이스나 추상 클래스 등을 활용해 기능을 확장할 수 있다.
  • 예시 (React):
    function Button({ onClick }) {
      return <button onClick={onClick}>Click me</button>;
    }
    
    // Different behaviors
    <Button onClick={() => console.log("Button 1 clicked")} />;
    <Button onClick={() => alert("Button 2 clicked")} />;

    동일한 <Button> 컴포넌트가 props를  받아 서로 다르게 동작하는것으로 이용할 수 있습니다.
    이를 통해 컴포넌트를 재사용하면서도 서로 다른 행동을 할 수 있게 합니다
    (예시로서 적절한지 모르겠습니다.. )

 

정리

  • 캡슐화는 객체의 데이터와 메서드를 하나로 묶고, 외부로부터 보호하는 개념.
  • 합성은 여러 객체를 조합해 새로운 기능을 만드는 방식으로, 상속보다 더 유연한 객체 재사용을 가능하게 함.
  • 다형성은 같은 인터페이스나 메서드를 사용하되, 객체마다 다른 방식으로 동작하는 특성으로, 유연성과 확장성을 높여줌.

이러한 개념들은 객체 지향 프로그래밍의 핵심 원칙으로, 더 나은 코드 설계와 유지보수를 가능하게 한다.

SOLID 원칙이란?

OOP 설계 원칙을 다섯가지로 나누어 설명한 것 입니다. 

  • S: 단일 책임 원칙 (Single Responsibility Principle, SRP)
  • O: 개방-폐쇄 원칙 (Open/Closed Principle, OCP)
  • L: 리스코프 치환 원칙 (Liskov Substitution Principle, LSP)
  • I: 인터페이스 분리 원칙 (Interface Segregation Principle, ISP)
  • D: 의존성 역전 원칙 (Dependency Inversion Principle, DIP)

위의 원칙들은 객체지향 설계를 더 나은 방향으로 이끌기 위해 만들어졌습니다.

 

 

그렇다면 SOLID 원칙은 왜 중요한가?

SOLID원칙 OOP 설계의 품질을 높이기 위한 가이드라인입니다.

  • 코드가 더 유연해지고, 유지보수가 용이해집니다.
  • 변경 사항이 발생해도 기존 코드에 영향을 최소화 할 수 있습니다.
  • 재사용성이 높아지고 버그가 적게 발생할 가능성이 높아집니다.

 

위에서 OOP(객체 지향적 프로그래밍)의 품질을 높이기 위한 가이드라인 이라는 말을 했지만, 프론트엔드에서도 리액트 컴포넌트 설계나 상태관리, API 연동 등에 SOLID 원칙을 적용하면 유지보수성 좋은 코드를 만들 수 있다고 합니다.

 

그럼 나도 정리를 해야지..

 

 

 

 

 

비동기와 동기는 웹 개발에서 중요한 개념입니다.

이 두 방식은 웹 애플리케이션에서 데이터를 주고받는 방법을 설명하며, 각각 고유한 장단점이 있어 상황에 따라 적절하게 사용합니다.

 

 

1. Sync(동기)

 

 - 요청과 응답이 동시에 이루어지는 방식입니다. 웹 페이지를 새로고침하면서 데이터를 불러오는 경우가 이에 해당합니다. 예를 들어, 온라인 쇼핑몰에서 사용자가 결제 정보를 입력하고 결제 버튼을 누르면, 페이지가 새로고침되면서 결제가 처리되고, 새 페이지에 결제 완료 정보가 표시됩니다.

 

장점

  1. 코드의 간결성
    - 동기 통신은 요청과 응답이 순차적으로 이루어지므로, 코드를 작성하고 이해하기 쉽습니다.
  2. 순서 보장
    - 요청을 보낸 후 응답을 받아야 다음 작음을 수행할 수 있으므로, 요청과 응답의 순서가 보장됩니다.
  3. 에러 처리 용이
    - 요청과 응답이 연결되어 있어, 에러 발생 시 처리하기 쉽습니다.

단점

  1. 성능 저하
    - 페이지 전체를 새로고침해야 하므로 서버와의 통신량이 많아지고, 성능이 저하될 수 있습니다.
  2. 사용자 경험 저하
    - 페이지가 깜빡거리거나 멈추는 형상이 발생하여, 사용자가 불편함을 느낄 수 있습니다.
  3. 작업 차단
    요청을 보낸 후 응답을 기다려야 하므로, 다른 작업을 동시에 수행할 수 없습니다.

 

2. Async(비동기)

 

 - 요청과 응답이 동시에 이루어지지 않는 방식입니다. 페이지를 새로고침하지 않고, 필요한 부분만 업데이트하는 경우가 이에 해당합니다. 예를 들어, 실시간 채팅 애플리케이션에서 메세지를 전송하면, 전체 페이지를 새로고침하지 않고 채팅 창에만 새 메시지가 나타납니다.

 

장점

  1. 성능 향상
    -  페이지의 일부만 업데이트하므로, 웹페이지의 속도와 성능이 향상됩니다.
  2. 사용자 경험 개선
    - 화면이 깜박거리거나 멈추지 않고 부드럽게 작동하여, 사용자 경험이 향상됩니다.
  3. 병렬 작업 가능
    - 요청을 보낸 후에도 다른 작업을 수행할 수 있습니다.

단점

  1. 코드 복잡도 증가
    - 비동기 통신은 코드 작성과 이해가 어려울 수 있으며, 복잡도가 증가할 수 있습니다.
  2. 순서 보장의 불확실성
    - 요청과 응답이 동시에 일어나지 않아, 요청의 처리 속도에 따라 응답순서가 뒤바뀔 수 있습니다.
  3. 에러 처리 복잡
    - 요청과 응답이 분리되어 있어, 에러가 발생했을 때 원인을 파악하고 처리하기가 어렵습니다.

3. 동기 및 비동기 처리의 예시

동기 처리의 예시

상품 상세 페이지 로딩

  • 설명: 사용자가 상품 목록에서 특정 상품을 클릭하면, 상품의 상세 정보 페이지로 이동합니다. 이때 전체 페이지가 새로고침되며, 새로운 URL로 이동하여 서버에서 상품에 대한 전체 HTML 페이지를 받아옵니다.
  • 동작: 전체 페이지가 서버로부터 응답을 받아 로딩되기 전까지 사용자는 다른 작업을 할 수 없습니다. 이는 요청이 완료될 때까지 기다려야 하기 때문입니다.
  • 예시 코드: 동기 방식의 폼 제출
    <form action="/order" method="POST">
        <input type="hidden" name="productId" value="12345">
        <button type="submit">Buy Now</button>
    </form>

    사용자가 Buy Now 버튼을 클릭하면, 페이지가 서버에 요청을 보내고 응답을 받아 새로고침됩니다. 이 과정은 동기적으로 진행되며, 서버 응답이 오기전까지 다른 작업을 할 수 없습니다.

비동기 처리의 예시

장바구니에 상품 추가

  • 설명 : 사용자가 특정 상품을 장바구니에 추가할 때, 페이지가 새로고침되지 않고, 해당 상품이 비동기적으로 서버에 추가 요청을 보낸 후, 장바구니의 상태를 업데이트합니다.
  • 동작 : 서버로부터 응답이 도착하기 전에도 사용자는 쇼핑몰에서 다른 상품을 검색하거나 추가할 수 있습니다. 서버에서 장바구니 업데이트 응답이 오면, 화면의 일부만 업데이트 응답이 오면, 화면의 일부만 업데이트되거나 알림이 표시됩니다.
  • 예시 코드: 비동기 방식의 상품추가
const addToCart = (productId) => {
    axios.post('/cart', { productId: productId })
        .then(response => {
            cartProductList(response.data);
        })
        .catch(error => {
            console.error('추가되지 않음:', error);
        });
};

사용자가  addToCart 버튼을 클릭하면, 페이지가 새로고침되지 않고 장바구니에 상품이 비동기적으로 추가됩니다.
서버 응답이 오면 List를 받아 추가 처리를합니다. 그 사이 사용자는 다른 작업을 할 수 있습니다.

'개발공부 > Web 관련' 카테고리의 다른 글

공부하며 정리해보기 - 1. REST API  (0) 2024.08.22
[Web] <meta> 태그  (0) 2023.03.02
[Web] 웹 이미지  (0) 2023.02.28

1. REST의 구성

1) 자원(RESOURCE) - URI (Uniform Resource Identifier)

 - 자원은 REST에서 관리하는 모든 데이터를 의미합니다. 에를 들어, 사용자 정보, 게시글, 상품 등… 다양한 내용이 자원이 될 수 있습니다. 자원은 특정 위치를 URI로 표현합니다.

 - URI란? : 인터넷 상에서 자원을 식별하는 고유한 주소입니다. 웹 브라우저에서는 URL 등으로도 많이 알려져 있습니다.

 URI를 통해 클라이언트는 서버의 자원에 접근할 수 있습니다.

 

예시 : /users/j-plum   - ID가 j-plum이라는 사용자에 대한 자원

/products/12345678  - ID가 상품번호가 12345678인 상품에 대한 자원.

(예시의 users와 products가 자원이되고 , 각각의 ID는 특정 자원을 식별하는 데 사용합니다.)

 

2) 행위(Verb) - HTTP Method

행위는 자원에 대해 수행할 동작을 정의합니다. REST 에서는 이 동작을 HTTP 메서드로 표현합니다.

HTTP 메서드는 클라이언트가 서버에 요청을 보낼 때 사용하는 규칙입니다.

 

- HTTP Method란? : HTTP Method는 클라이언트 서버 간의 상호작용을 정의하는 표준 방법입니다.

 

  • GET : 자원을 조회할 때 사용합니다. (서버의 데이터를 가져올 목적)
    • 예시 :  GET /users/j-plum - ID가 j-plum인 사용자의 정보를 가져옵니다.
  • POST : 새로운 자원을 생성할 때 사용합니다.
    • 예시 :  POST /users - 새로운 사용자를 생성합니다.
  • PUT  : 자원을 업데이트(수정)할 때 사용합니다.
    • 예시 : PUT /users/j-plum  - ID가 j-plum인 사용자의 정보를 수정합니다.
      같은 URI라도 HTTP 메서드(GET, POST, PUT, DELETE)에 따라 서버가 수행하는 작업은 다릅니다
  • DELETE : 자원을 삭제할  때 사용합니다.
    • 예시 : DELETE /users/j-plum -  ID가 j-plum인 사용자를 삭제합니다.

추가 내용

- 동일한 URI를 사용하더라도 HTTP 메서드에 따라 서버가 수행하는 작업은 다릅니다

( 위의 예시는 /users/j-plum 라는 URI를 반복해서 사용하고 있습니다. )

 

동일한 URI를 사용하는 장점

 

 

장점

  1. 간결함 : 동일한 URI를 반복적으로 사용하면서, URI를 간소화하고 가독성을 높일 수 있습니다. 이로 인해 URI의 구조가 복잡해지지 않으며, 이해하기 쉬운 API를 설계할 수 있습니다.
  2. 의미 전달 : URI와 HTTP 메서드를 조합함으로써, 자원에 대한 조작 의도를 명확하게 전달합니다. 예를 들어, /users/j-plum URI는 특정 사용자를 나타내며, GET, PUT, DELETE 메서드를 통해 조회, 수정, 삭제와 같은 다양한 작업을 수행할 수 있음을 직관적으로 알 수 있습니다.
  • GET /users/j-plum: j-plum이라는 사용자의 정보를 조회합니다.
  • PUT /users/j-plum: j-plum이라는 사용자의 정보를 수정합니다.
  • DELETE /users/j-plum: j-plum이라는 사용자를 삭제합니다.

 

 

3) 표현(Representation) 

 - 표현은 자원의 상태나 데이터를 전달하는 방식을 의미합니다. 서버는 클라이언트의 요청에 따라 자원의 상태를 다양한 형식으로 반환할 수 있습니다. 주로 JSON이나 XML형식으로 표현됩니다.

 

 - 표현란? : 표현은 자원의 실제 데이터를 포험하며, 이를 통해 클라이언트는 서버로부터 자원의 상태를 확인하거나 필요한 데이터를 받을 수 있습니다. 표현은 자원의 구조와 내용을 설명합니다.

 

 

예시 :  만약 사용자가 /users/j-plum URI에 GET 요청을 보내면, 서버는 ID가 123인 사용자의 정보를 반환합니다. 

{
  "id": j-plum,
  "name": "JJ",
  "email": "j.plum@gmail.com"
}

 

 



2. REST의 특징

1) Uniform Interface (유니폼 인터페이스)

 - Uniform Interface는 클라이언트가 서버 리소스와 상호작용하는 방식을 일관성 있게 정의합니다. 이로 인해 클라이언트는 URI로 지정된 리소스에 대한 작업을 통일되고 제한적인 인터페이스를 통해 수행할 수 있습니다.

 

예시 :  만약 사용자가 /users/ j-plum URI에 GET 요청을 보내면, 서버는 ID가 j-plum인 사용자의 정보를 반환합니다. 이때 모든 리소스에 대해 GET, POST, PUT, DELETE와 같은 HTTP 메서드가 일관되게 사용됩니다.


2) Stateless (무상태성)

 - REST는 무상태성을 지니고 있어 서버는 클라이언트의 이전 요청에 대한 정보를 저장하지 않습니다. 즉, 각 요청은 독립적으로 처리되며, 서버는 요청을 받을 때 필요한 모든 정보를 요청 본문이나 URL에서 받아야 합니다.

 만약 사용자가 POST /login 요청을 통해 로그인했다면, 서버는 이후 요청에서 이 사용자의 로그인 상태를 기억하지 않습니다. 클라이언트는 이후의 요청에서 인증 토큰 등을 헤더에 포함시켜야 합니다.

 

조금 더 자세하게 정리해 보겠습니다.

 

REST의 무상태성 원칙은, 서버가 클라이언트의 상태를 저장하지 않는다는 것을 의미합니다.

이 원칙에 따라, 각 요청은 독립적이며 자급자족적인 정보를 포함해야 합니다. 서버는 이전 요청의 상태나 세션정보를 기억하지 않으며, 클라이언트로부터 받은 각 요청을 그 자체로 완전한 정보로써 처리합니다.


 

1. 독립적  : 독립적이라는 것은 각 요청이 다른 요청과 연관되지 않는다는 것을 의미합니다. 하나의 요청이 다른 요청의 상태나 결과에 의존하지 않는다는 뜻 입니다.

 

2. 자급자족적  : 각 요청이 서버가 요청을 처리하는 데 필요한모든 정보를 포함하고 있어야 한다는 것을 의미합니다.

서버는 그 요청만으로 작업을 수행할 수 있어야 하며, 추가적인 상태 정보를 기억할 필요가 없습니다.

 

 


여기서 의문점이 생겼습니다

어... GET /users/j-plum 를 통해 j-plum 의 정보를 가져와사 그 정보를 이용해서 다른 API를 이용한다면

독립적인게 아니지않나..? 서로 의존하는 것 같은데.??

 

이는 클라이언트의 관점에서 입니다.


REST 의 무상태성(Statless)의 원칙과 독립성은 서버 관점에 바라봐야 합니다.

 

클라이언트 입장에서는 첫 번째 호출에서 얻은 데이터를 통해 두 번째 API를 호출하기 떄문에, 두 요청이 서로 의존하는 것 처럼 보일 수 있습니다.

 

서버 입장에서는, 각각의 요청이 독립적이고 자급자족적입니다.

서버는 첫번째 요청과 두번째 요청이 서로 관련이 있는지 알 필요가 없고, 기억하지 않습니다.

서버는 필요한 정보를 해당 요청에서 직접 받아 처리합니다.

 



 

3) Cacheable (캐시 가능)

 -  RESTful 서비스는 HTTP 프로토콜을 기반으로 작동합니다. HTTP는 웹 브라우저와 서버가 데이터를 주고 받는 방식에 대한 규칙입니다. 그 중 하나가 캐시(cache) 기능입니다.

 

캐시란?

- 캐시는 자주 사용하는 데이터를 임시로 저장해 두었다가, 다시 필요할 때 빠르게 꺼내 쓰는 메커니즘입니다.

예를들어, 웹 사이트에 접속할 때 이미 한 번 본 이미지를 매번 서버에서 다시 받아오지 않고, 브라우저가 임시로 저장해 둔 이미지를 재사용 하는것이 캐시 입니다.

 

그렇다면 RESTful 서비스에서의 캐시는?

 - RESTful API는 이런 캐시 기능을 활용할 수 있습니다. 서버는 리소스가 캐시될 수 있는지를 브라우저에게 알려줍니다.

이때 사용되는 것이 HTTP 헤더입니다

 

HTTP 헤더란?

 - HTTP 헤더는 클라이언트(브라우저)와 서버간의 통신에서 추가적인 정보를 담은 메세지 입니다. 

요청이나 응답과 함께 전달됩니다.

 

캐시 관련 HTTP 헤더 예시

  • Chahe-control : 서버가 브라우저에게 리소스를 캐시할 수 있는지, 캣할 수 있다면 얼마나 오래 저장할 수 있는지를 알려줍니다.
    • 예시) Cache-Control : max-age=3600   => 이 리소스를 1시간동안  캐시할 수있다는 의미입니다.
  • Expires : 캐시된 리소스가 언제 만료되는지를 나타냅니다.
    • 예시) Expires : wed, 21 Oct 2024 01:01:00 GMT   => 이 시간 이후로는 캐시를 사용하지 않고, 서버에서 다시 받아야 한다는 의미입니다.

 

상황 예시

 

상황 1 : 자원 요청하기

  1. 사용자가 웹사이트에서 제품 목록을 보려고 합니다.
  2. GET /products 를 서버에 요청
  3. 서버는 제품 목록을 응답하며, Cache-Control : max-age=600 이라는 HTTP 헤더도 같이 보냅니다.(10분간 캐시가능)
  4. 브라우저는 10분 동안 이 데이터를 저장해두고, 그 시간 내에 다시 요청이 오면 서버에 가지 않고 저장된 데이터를 바로 보여줍니다.

캐시된 데이터를 사용하는 과정

  • 사용자가 10분 이내에 다시 동일한 GET /products 요청을 보낸다면, 브라우저는 먼저 캐시를 확인합니다.
  • 캐시에서 GET /products 라는 키를 찾습니다. 이 가 존재하고, 캐시된 데이터가 여전히 유효하면, 브라우저는 서버에 요청을 보내지 않고 바로 캐시된 데이터를 사용자에게 보여줍니다.
  • 이로 인해 페이지 로딩 속도가 빨라지고, 서버의 부하도 줄어드는 장점이 있습니다.

캐시 만료 후

  • 설정된 유효 시간이 만료되면 더 이상 유효하지 않으므로, 브라우저는 다시 서버에 GET /products 요청을 보내고,
    최신 데이터를 받아옵니다.
  • HTTP 헤더의 내용에 따라 다시 캐

 

3줄요약

  1. 브라우저는 GET /products 라는 요청을 키로, 응답 데이터를 값으로 캐시에 저장힙니다.
  2. 동일한 요청이 시잔 내에 발생하면, 브라우저는 캐시에 저장된 데이터를 서버에 다시 요청하지 않고 바로 사용자에게 제공합니다.
  3. 이는 서버와 브라우저가 캐시 관련 정보를 HTTP 헤더를 통해 주고받으면서 이루어지는 내용입니다.

 

 


4) Self-descriptiveness (자체 표현 구조)

 -  REST API 메시지는 자체 설명 구조를 가지고 있어, 클라이언트가 별도의 문서 없이도 API의 요청 및 응답을 이해할 수 있습니다. 이 구조 덕분에 시스템은 더 직관적이고 유지보수가 쉬워집니다.

 

예시 : 서버가 JSON 형식으로 {"userId": j-plum, "name": "Euang", "role": "admin"}와 같은 응답을 반환하면, 클라이언트는 이 메시지의 내용만 보고도 어떤 데이터가 포함되어 있는지 쉽게 이해할 수 있습니다.


5) Client-Server 구조

 - REST 아키텍처는 클라이언트와 서버를 분리된 구조로 설계합니다. 클라이언트는 사용자 인터페이스 및 사용자 경험을 담당하고, 서버는 데이터 저장 및 비즈니스 로직을 처리합니다. 이로 인해 두 시스템의 독립성이 높아집니다.

 

예시 : 클라이언트 애플리케이션은 Angular나 React 같은 프론트엔드 프레임워크로 구현되고, 서버는 Node.js나 Django 같은 백엔드 프레임워크로 구현될 수 있습니다. 클라이언트와 서버는 명확히 분리되어 있어 각자의 역할에 집중할 수 있습니다.


6) 계층형 구조

 - REST 서버는 여러 계층으로 구성될 수 있으며, 이를 통해 보안, 로드 밸런싱, 암호화 등 다양한 기능을 추가할 수 있습니다. 이 구조는 클라이언트와 서버 사이에 프록시나 게이트웨이 같은 중간 매체를 사용할 수 있게 합니다.

 

예시 : 클라이언트는 서버와 직접 통신하는 대신, 먼저 게이트웨이 서버를 통해 요청을 보냅니다. 이 게이트웨이 서버는 요청을 처리하거나 추가적인 보안 계층을 추가한 후 실제 서버로 요청을 전달합니다.

 

 

inpa dev - rest api 정리

https://inpa.tistory.com/entry/WEB-%F0%9F%8C%90-REST-API-%EC%A0%95%EB%A6%AC

 

[WEB] 🌐 REST API 구성/특징 총 정리

REST API의 탄생 REST는 Representational State Transfer라는 용어의 약자로서 2000년도에 로이 필딩 (Roy Fielding)의 박사학위 논문에서 최초로 소개되었습니다. 로이 필딩은 HTTP의 주요 저자 중 한 사람으로 그

inpa.tistory.com

 

 

 

'개발공부 > Web 관련' 카테고리의 다른 글

공부하며 정리해보기 - 2. Async/Sync에 대해  (0) 2024.09.10
[Web] <meta> 태그  (0) 2023.03.02
[Web] 웹 이미지  (0) 2023.02.28

리엑트는 왜 나왔을까?

 

1. 페이지 전환없이 앱 같은 느낌을 원할 경우(예시 : gamil(AJAX))

 

2. 한 페이지에서 다 하는 SPA(single page application) 방식의 유행

 -  SPA는 데이터 유지에 용이. (컨텐츠만 갈아 끼우는 느낌의 방식으로 자주 이용합니다)

 

3. 프론트의 비중이 점점 높아짐(데이터를 많이 다룹니다) 

 - 2번에서 말한 데이터 유지를 통해 백엔드에게 추가 요청이 줄어듭니다.

 

4. MVC 등의 개발 방식은 대규모 개발에 적합하지 않아 대안이 필요

 - MVC는 backend에서 사용하던 아키텍처 입니다. (Model 과 view의 양방향 바인딩입니다.)

 - 잦은 리랜더링이 발생할 수 있으며 MVC 구현에서는 불필요한 업데이트와 렌더링이 발생하여 성능 문제가 발생할 수 있습니다. 특히 대규모 애플리케이션에서는 이러한 성능 이슈가 더욱 부각될 수 있습니다.

 

5. React는 FLUX패턴

 - 단방향 바인딩입니다. 데이터 변경시 흐름 추적이 수월해집니다.

 - 대규모 웹 사이트에 적합합니다.

 

6. 컴포넌트 기반의 아키텍처

 - 모듈화된 개발을 가능케 하고 코드의 재사용성이 증가합니다.  프론트엔드 개발의 복잡성을 관리하는 데 도움이 됩니다

 

7.가독성과 생산성 향상

 - JSX라는 문법을 도입하여 가독성을 높이고, 컴파일러를 통해 JavaScript 코드로 변환함으로써 생산성을 향상시켰습니다.

 

7. 커뮤니티와 생태계

 - React는 넓은 커뮤니티와 다양한 생태계를 가지고 있어서 개발자들 간의 지원과 자료 공유가 활발하게 이루어집니다.

(특히 이부분은 정말 차이가 심한 것 같습니다.)

 

'개발공부 > 기타' 카테고리의 다른 글

npm 과 package.json  (0) 2023.03.31
프로그래밍 표기법  (0) 2023.03.10
상대 경로와 절대경로  (0) 2023.03.02

+ Recent posts