모노레포란?

모노레포(Monorepo)를 간단히 말하면, 서버와 클라이언트 코드가 한 저장소(Repository)에 같이 있는 것을 뜻한다

각각의 코드베이스를 별도의 저장소로 관리하는 멀티레포(Multirepo)와는 달리, 하나의 저장소에서 모든 관련 프로젝트를 통합적으로 관리하는 방식이다

모노레포의 장점

  • 코드 공유: 서버와 클라이언트가 같은 저장소에 있으므로, 공통된 코드를 쉽게 재사용
  • 일관된 버전 관리: 한 저장소에서 관리되므로 의존성 버전 충돌을 줄임
  • 일관된 개발 환경: 서버와 클라이언트 간의 개발 환경을 동일하게 유지
  • 간편한 CI/CD: 한 저장소에서 빌드와 테스트 파이프라인 설정 과정 단순화

모노레포의 단점

  • 저장소 크기 증가: 대규모 프로젝트에서 저장소 크기가 커지고 클론, 빌드, 테스트 시간이 증가
  • 의존성 관리 복잡성: 하나의 프로젝트에서 특정 의존성을 업데이트시 다른 프로젝트에 영향을 미칠 수 있음
  • 권한 관리 어려움: 모든 프로젝트가 한 저장소에 있으므로 프로젝트별 접근 권한 관리가 어려움

해결 방안

  • Monorepo 도구 사용: Nx, Bazel 같은 도구를 사용해 필요한 부분만 빌드, 테스트
  • 의존성 관리: Lerna, Yarn Workspaces 등을 통해 독립적인 의존성 관리 구현
  • 권한 분리: GitHub CODEOWNERS 파일로 디렉토리별 권한 설정

멀티레포란?

멀티레포(Multirepo)는 각 프로젝트를 독립적으로 별도의 저장소에서 관리하는 방식을 뜻한다

서버와 클라이언트 코드, 공통 라이브러리 등이 각기 다른 저장소에 저장되며, 필요할 때만 의존성을 가져와 사용하는 형태이다

멀티레포의 장점

  • 독립적인 배포 및 개발: 특정 프로젝트만 선택적으로 업데이트하거나 배포하기 쉬움 (각 팀이 다른 팀의 코드베이스에 의존하지 않고 빠르게 개발 및 배포 가능)
  • 프로젝트 분리: 각 코드베이스가 독립적으로 관리되므로 프로젝트 간 영향도 낮아짐 하나의 저장소에서 발생한 문제가 다른 프로젝트에 영향을 주지 않음
  • 다양한 기술 스택 관리: 서로 다른 언어나 빌드 도구를 사용하는 프로젝트 간 충돌을 방지

멀티레포의 단점

  • 공통 코드 관리 어려움: 공통 라이브러리나 설정을 동기화하기 까다로움
  • 의존성 관리 복잡: 공통으로 사용하는 라이브러리나 설정 파일을 여러 저장소에 중복 관리해야 할 때 버전 충돌 가능성 있음 (A와 B 프로젝트가 동일한 공통 라이브러리를 사용하는데, A는 라이브러리의 최신 버전을 요구하고 B는 이전 버전을 유지해야 할때)

해결방안

  • 공통 코드 관리: 각 저장소에서 사용하는 공통 코드를 별도의 라이브러리로 추출하고, NPM 패키지나 PyPI 같은 패키지 관리 시스템에 배포하여 버전 관리를 체계적으로 수행
  • 의존성 관리 자동화: Dependabot이나 Renovate 같은 도구를 사용해 각 저장소의 의존성을 자동으로 업데이트하고 충돌을 최소화

모노레포 vs 멀티레포

  모노레포 멀티레포
저장소 구조 하나의 저장소 여러 개의 저장소
공통 코드 관리 쉬움 어려움
프로젝트 독립성 낮음 높음
CI/CD 설정 간단 프로젝트별 설정 필요

모노레포를 사용하는 회사

  • Google: 대규모 코드를 모노레포 방식으로 관리하며, Bazel 같은 빌드 도구를 사용
  • Facebook: 모든 프로젝트를 모노레포 방식으로 관리하여 일관된 코드베이스 유지
  • Uber: 여러 플랫폼에서 공통 코드를 효율적으로 관리하기 위해 모노레포를 채택

멀티레포를 사용하는 회사

  • Amazon: 서비스가 분리된 독립적인 팀 구조(소위 "Two Pizza Team")를 기반으로 멀티레포를 사용
  • Netflix: 독립된 마이크로서비스 아키텍처를 채택하여 각 서비스가 별도의 저장소에서 관리됨
  • Microsoft: Windows, Office 등 여러 제품군을 개별적으로 관리하기 위해 멀티레포를 활용

모노레포와 멀티레포의 선택 기준

  • 모노레포: 프로젝트 간 강한 연관성이 있거나, 공통 코드를 많이 공유해야 하는 경우
  • 멀티레포: 각 프로젝트가 독립적이며, 팀별로 자유로운 기술 선택과 관리가 필요한 경우

장단점이 명확하기 때문에, 회사의 팀 구조와 프로젝트 요구 사항에 따라 선택하는것이 좋겠다!

(상세한 내용은 참고 자료 링크 모던 프론트엔드 프로젝트 구성 기법 - 모노레포 개념 편에 더 자세히 나와있다)

 

참고자료, 이미지 출처

https://d2.naver.com/helloworld/0923884

기상청 공공 API 쓰는 법

  1. 행정안전부 공공데이터 포털 접속
    https://www.data.go.kr/
  2. 기상청 단기예보 조회서비스 검색, 오픈 API 목록에 있다
    https://www.data.go.kr/data/15084084/openapi.do
  3. 활용 신청 및 승인
    1. 로그인 / 회원가입
    2. API 활용 사유만 쓰면 활용신청 가능, 조금 기다리면 바로 승인됨
  4. 엔드 포인트와 인증키 메모
  5. API 호출 함수 생성
    fetchWeather() 함수를 만들었다
  6. 날씨 데이터를 표시할 모달 컴포넌트 생성
  7. 아이콘 클릭시 API 호출 및 모달 표시

현재 위치에 따라 날씨 데이터 가져오기

아래는 내 프로젝트에서 사용된 WeatherModal 컴포넌트 코드로 리액트, 타입스크립트, 테일윈드를 사용하고 있다

주석에 설명을 추가하였다

import React, { useEffect, useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
  faTimes,
  faMapMarkerAlt,
  faThermometerHalf,
  faTint,
  faWind,
} from "@fortawesome/free-solid-svg-icons";

interface WeatherModalProps {
  isOpen: boolean;
  onClose: () => void;
}

const WeatherModal: React.FC<WeatherModalProps> = ({ isOpen, onClose }) => {
  const [weatherData, setWeatherData] = useState<any>(null);
  const [isLoading, setIsLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);
  const [location, setLocation] = useState<{ nx: number; ny: number } | null>(
    null
  );

  useEffect(() => {
    if (isOpen) {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          const { latitude, longitude } = position.coords;
          const { nx, ny } = convertToGridCoords(latitude, longitude);
          setLocation({ nx, ny });
        },
        (error) => {
          console.error("Error getting location:", error);
          setError("위치를 가져올 수 없습니다.");
        }
      );
    }
  }, [isOpen]);

  useEffect(() => {
    if (isOpen && location) {
      fetchWeather(location.nx, location.ny);
    }
  }, [isOpen, location]);

  const fetchWeather = async (nx: number, ny: number) => {
    setIsLoading(true);
    setError(null);
    try {
      const apiKey =
        "vk4aWLTlPOiSCrGPCqW9+RPDwCEbd2qZroyXnqkjhfKAQM15ca8W2DHua28iBIE0OaNWNEPaaAR2suzllAcotg==";
      const encodedKey = encodeURIComponent(apiKey);

      const today = new Date(); // 오늘의 날짜 객체 생성
      const hour = today.getHours(); // 현재 시각 불러오기

      let formattedTime = hour.toString().padStart(2, "0") + "00"; // API에서 요구하는 HH00 형식으로 변환

      const formattedDate = today.toISOString().slice(0, 10).replace(/-/g, "");

      const baseUrl =
        "https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst";
      const queryParams = `numOfRows=10&pageNo=1&base_date=${formattedDate}&base_time=${formattedTime}&nx=${nx}&ny=${ny}&dataType=JSON`;

      const url = `${baseUrl}?serviceKey=${encodedKey}&${queryParams}`;

      const response = await fetch(url);
      const data = await response.json();

      if (data.response && data.response.body && data.response.body.items) {
        const items = data.response.body.items.item;
        const processedData = processWeatherData(items);
        setWeatherData(processedData);
      } else {
        throw new Error("예상치 못한 API 응답 구조");
      }
    } catch (error) {
      console.error("날씨 데이터를 가져오는 중 오류 발생:", error);
      setError(
        error instanceof Error
          ? error.message
          : "알 수 없는 오류가 발생했습니다."
      );
    } finally {
      setIsLoading(false);
    }
  };

  const processWeatherData = (items: any[]) => {
    const processed: any = {};
    items.forEach((item) => {
      switch (item.category) {
        case "T1H": // 기온
          processed.temperature = parseFloat(item.obsrValue);
          break;
        case "RN1": // 강수량
          processed.rainfall = parseFloat(item.obsrValue);
          break;
        case "REH": // 습도
          processed.humidity = parseFloat(item.obsrValue);
          break;
        case "WSD": // 풍속
          processed.windSpeed = parseFloat(item.obsrValue);
          break;
      }
    });
    return processed;
  };

	// 위치 변환 함수 : 사용자의 현재 위치(위도 및 경도)를 기상청 API가 요구하는 격자 좌표(nx, ny)로 변환하는 함수
  const convertToGridCoords = (lat: number, lon: number) => {
    return { nx: 55, ny: 127 }; // 격자 좌표로 변환하는 예시 좌표 반환
  };

  if (!isOpen) {
    return null;
  }

  return (
    <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
      <div className="bg-white p-6 rounded-lg max-w-sm w-full">
        <div className="flex justify-between items-center">
          <h2 className="text-xl font-bold">현재 날씨</h2>
          <button
            onClick={onClose}
            className="text-gray-500 hover:text-gray-700"
          >
            <FontAwesomeIcon icon={faTimes} />
          </button>
        </div>
        <FontAwesomeIcon icon={faMapMarkerAlt} className="mr-2" />
        <span>현재 위치</span>
        {isLoading ? (
          <p>날씨 데이터를 불러오는 중...</p>
        ) : error ? (
          <p className="text-red-500">오류: {error}</p>
        ) : weatherData ? (
          <div>
            <div className="flex justify-between gap-2 mt-2">
              <div className="flex-grow basis-0 bg-blue-100 p-3 rounded-lg">
                <FontAwesomeIcon
                  icon={faThermometerHalf}
                  className="mr-2 text-blue-500"
                />
                <span className="font-bold">{weatherData.temperature}°C</span>
              </div>
              <div className="flex-grow basis-0 bg-blue-100 p-3 rounded-lg">
                <FontAwesomeIcon icon={faTint} className="mr-2 text-blue-500" />
                <span className="font-bold">{weatherData.humidity}%</span>
              </div>
              <div className="flex-grow basis-0 bg-blue-100 p-3 rounded-lg">
                <FontAwesomeIcon icon={faWind} className="mr-2 text-blue-500" />
                <span className="font-bold">{weatherData.windSpeed}m/s</span>
              </div>
              {weatherData.rainfall > 0 && (
                <div className="bg-blue-100 p-3 rounded-lg">
                  <FontAwesomeIcon
                    icon={faTint}
                    className="mr-2 text-blue-500"
                  />
                  <span className="font-bold">{weatherData.rainfall} mm</span>
                </div>
              )}
            </div>
          </div>
        ) : (
          <p>날씨 데이터가 없습니다.</p>
        )}
      </div>
    </div>
  );
};

export default WeatherModal;

근데 이렇게 하면 날씨가 개떡같이 나올 것이다

현재 위치를 00시 00구 00동으로 나타내려면 역지오코딩(Reverse Geocoding)이라는 기술을 사용해야한다

그건 다음 포스팅에서...

간단함

마이페이지에서 찾지말고 자격증. 확인서 메뉴에서 확인서 발급 신청을 하면 된다

마이페이지에서 자격증 접수내역과 같이 확인서를 발급 받도록 하면 훨씬 편할텐데

이 화면 다음으로 넘어가면 확인서 발급이 가능한 봤던 시험 목록이 나온다

체크 박스가 있는데, 한번에 1개만 선택 가능해서 여러개 발급받으려면 이 과정을 반복해야한다

참고로 응시확인서는 합격자 발표 이후 가능하므로 10월에 본 실기의 발표는 12월이라 글 작성 시점인 11월에는 응시확인서를 발급 받을 수 없었다 (시험접수확인서는 가능하나, 응시 확인서라는게합격 확인서가 아닌데 왜 이런 시스템인진 모르겠다)

대체로 좋지 못한 UX였다

정보처리기사 진법 변환을 공부하다 문득 든생각

애초에 뭔 진법이 이렇게 많냐 이거 왜쓰는거야?

10진수

우리의 손가락을 열개다

그 덕분에 우리는 숫자를 0부터 9까지 쉽게 셀 수 있다

이게 우리가 십진수를 쓰게 된 유래다

2진수

컴퓨터는 0과1로만 이루어져있다

는 사실을 알고는 있지만 컴퓨터로 0123456789 텍스트를 치면 다 나오는데

근본적으로 0과 1로 변환되어야 할 이유는 무엇일까

컴퓨터가 데이터를 보는 방법이 그 이유다

컴퓨터는 전압을 올리거나 내리는 방식으로 데이터를 표현한다

전압이 있을 때는 1, 없을 때는 0으로 표시하는 방식이 가장 간단하고 안정적이다

이렇게 그래프 상으로는 전류가 끊긴 것 같지만

사실은 이런식으로 되어있는 형태이다

 

그리고 각각의 자릿수를 Bit 비트 라고 한다

즉, 1 bit 로 표현할 수 있는것은 참, 거짓 딱 두가지이다

 

여기서 2비트로 늘리면 표현할수 있는 가지수는 2의 2승으로 4가지로 늘어난다 (0부터 3)

4비트는 2의 4승으로 16가지의 표현이 가능하다 (0부터 15)

8비트는 2의 8승으로 256가지의 표현이 가능하다 (0부터 255)

 

컴퓨터는 이 8비트를 데이터가 저장되는 최소 단위로 삼기로 했다

4개로 나뉘어진 비트의 단위를 Nibble이라하며, 앞4비트와 뒤4비트가 합쳐진 8비트를 1Byte 라고 한다

1 byte = 8bits
           = 2nibble ( 4bits : 4bits)
           = 2 ^ 8 = 256 (0~255)

16진수

앞서 4비트는 16가지의 표현이 가능하다 했다

기존의 10진수로는 0부터 9까지 한자리수로 표현이 가능한데, 10이상의 숫자까지 한자리로 표현할 수가 없었다. 여기서 16진수가 등장한다

10이상의 숫자가 나오면 10 = a, 11 = b, 12 = c, 13 = d, 14 = e, 15 = f 이렇게 알파벳으로 표현하게 되었다

예를 들어, 10진수 255는 2진수로 11111111, 16진수로는 FF로 표현된다

왜 굳이 다양한 진법을 사용할까?

16진수를 사용하면 큰 수를 더 간결하고 알아보기 쉽게 표현할 수 있기 때문에 메모리 주소, 색상 코드, 파일 형식 등에서 자주 사용된다

2진수는 컴퓨터 내부에서 데이터를 처리하는 데 필수적이지만, 사람이 직접 읽고 쓰기에는 불편하다

그래서 8진수16진수처럼 더 큰 단위를 사용해 데이터를 간단히 표현할 수 있다

특히 16진수는 4비트 단위를 깔끔하게 나눌 수 있어 데이터의 경계를 명확히 볼 수 있다

8bits = 1 byte : C언어로 char
16bits = 2 byte : C언어로 short int
32bits = 4 byte : C언어로 int
64bits = 8 byte : C언어로 long int

더 큰 단위 또한 있다

1 byte
1000 byte = 1 Kbyte (10^3)
1000 Kyte = 1 Mbyte (10^6)
1000 Myte = 1 Gbyte (10^9)
1000 Gyte = 1 Tbyte (10^12)

8진수

8진수는 2진수보다 더 짧고 간결하게 데이터를 표현할 수 있는 방법이다

예를 들어, 3비트0부터 7까지 8개의 값을 표현할 수 있다

8진수는 예전 컴퓨터 시스템에서 메모리나 데이터 표현을 단순화하기 위해 자주 사용되었다

 

정리하자면 진법이 다양한 이유는 결국 컴퓨터의 효율성사람의 편의성을 동시에 만족시키기 위해서라고 볼 수 있겠다

 

참고 자료

https://youtu.be/lZKej5s3T_w?si=Da7ZiAMwnwQFkbcg

서버 배포 후 클라이언트의 로컬 환경에서 세션을 이용한 로그인 기능이 제대로 동작하지 않는 문제가 발생했다

확인해 보니, 서버에 세션 쿠키가 저장되지 않는 것 같았다

이 문제를 해결하기 위해 쿠키의 속성에 대해 더 자세히 알아보고 설정을 수정하게 되었다

Cookie

쿠키는 웹 애플리케이션에서 중요한 정보를 저장하고 전송할 때 사용되며, 이를 안전하게 다루기 위해 다양한 속성들을 설정할 수 있다

Secure

Secure 속성은 HTTPS 통신에서만 쿠키에 접근할 수 있도록 제한한다

이를 통해 악의적인 네트워크 공격(예: 중간자 공격)을 방지할 수 있다

(localhost 환경에서는 Secure 속성을 지정해도 HTTP로 통신이 가능하지만, 실제 배포 환경에서는 HTTPS를 사용해야한다)

SameSite

SameSite 속성은 크로스 사이트 요청에서 쿠키 전송을 제어하는 역할을 한다

이를 통해 사이트 외부에서 발생한 요청에 대한 쿠키 전송을 제한함으로써, CSRF(Cross-Site Request Forgery) 공격을 방지할 수 있다

Strict

Strict로 설정된 쿠키는 해당 도메인 내에서 발생한 요청(퍼스트 파티 쿠키)만 전송된다

즉, 외부에서 오는 모든 크로스 사이트 요청에서는 쿠키가 전송되지 않는다

예를 들어, 외부 링크나 다른 도메인에서의 접근을 통해 사용자가 사이트로 돌아올 때 쿠키가 전송되지 않는다

Lax

Lax 모드는 기본적으로 Strict와 비슷하지만, 일부 안전한 요청 (GET 메서드를 통한 외부 링크 클릭 등)에서는 쿠키 전송이 허용된다

예를 들어, 사용자가 다른 사이트에서 내 사이트로 링크를 클릭할 때는 쿠키가 전송된다

None

None으로 설정하면 동일 사이트와 크로스 사이트 모두에서 쿠키 전송이 가능하다

즉, 퍼스트 파티 쿠키와 서드 파티 쿠키가 모두 전송될 수 있어, CSRF 공격에 취약하다

크롬 80 버전 이후부터는 SameSite=None으로 설정된 쿠키는 반드시 Secure 속성과 함께 사용해야 하며, 그렇지 않으면 브라우저에서 쿠키를 차단한다

HttpOnly

HttpOnly 속성은 클라이언트의 자바스크립트로 쿠키에 접근할 수 없도록 막아준다

이를 통해 XSS(Cross-Site Scripting) 공격으로부터 쿠키 탈취를 방지할 수 있다

브라우저에서만 쿠키를 처리할 수 있도록 하여, 클라이언트 측 코드가 쿠키를 조작하지 못하게 하는 것이다

 

Hyper Text Transfer Protocol HTTP

HTTP 상태 코드(HTTP Status Codes)는 클라이언트와 서버 간의 통신 상태를 나타내는 3자리 숫자로 구성된 코드로, 클라이언트가 다음에 취해야 할 동작을 결정하는 중요한 역할을 한다

상태 코드는 1XX부터 5XX까지 다양한 범주로 나뉘며, 각각 클라이언트와 서버 간의 통신 과정에서 발생하는 특정 상황을 나타낸다

1XX: 정보 응답

  • 100 (계속): 클라이언트가 요청의 나머지 부분을 계속 전송해야 함을 의미
  • 101 (프로토콜 전환): 서버가 클라이언트의 프로토콜 전환 요청을 승인
  • 102 (처리 중): 요청을 처리 중이며 응답이 지연됨을 알림

2XX: 성공 응답

  • 200 (성공): 요청이 성공적으로 처리됨
  • 201 (작성됨): 요청이 성공적으로 처리되었고, 서버가 새로운 리소스를 생성함 ex) 회원 가입 후 새로운 사용자 계정 생성
  • 202 (허용됨): 요청을 접수했지만 아직 처리되지 않음
  • 204 (콘텐츠 없음): 요청이 성공했으나 응답 본문이 없음 ex) 데이터 삭제 요청 후 별도의 응답 데이터가 필요 없을 때

3XX: 리다이렉션 응답

  • 301 (영구 이동): 요청한 리소스가 영구적으로 새로운 URL로 이동됨
  • 302 (임시 이동): 요청한 리소스가 일시적으로 다른 URL에서 제공됨 ex) 사용자를 다른 페이지로 임시 리다이렉트할 때
  • 304 (수정되지 않음): 리소스가 변경되지 않았으며, 캐시된 버전을 사용 가능

4XX: 클라이언트 오류

  • 400 (잘못된 요청): 서버가 요청을 이해할 수 없거나 잘못된 데이터를 받았을 때 사용
  • 401 (권한 없음): 인증이 필요하지만 제공되지 않았거나 실패한 경우 ex) 로그인 없이 보호된 리소스에 접근 시도
  • 403 (금지됨): 서버가 요청을 거부, 인증은 성공했으나, 클라이언트에게 해당 리소스에 대한 접근 권한이 없는 경우
  • 404 (찾을 수 없음): 요청한 리소스를 찾을 수 없음 ex) 잘못된 URL 요청

5XX: 서버 오류

  • 500 (내부 서버 오류): 서버에서 처리 중 예기치 않은 오류 발생
  • 502 (잘못된 게이트웨이): 게이트웨이 또는 프록시 서버가 상위 서버로부터 잘못된 응답을 받음
  • 503 (서비스 이용 불가): 서버가 일시적으로 요청을 처리할 수 없음 (과부하 상태이거나 유지보수 중일 때)

보통 API 응답에서 사용되는 주요 상태 코드들만 다루었다

오버로딩과 오버라이딩은 객체지향 프로그래밍(OOP)에서 메서드를 다루는 두 가지 중요한 방법이다. 이 두 개념을 제대로 이해하려면 객체지향의 몇 가지 특징을 먼저 짚고 넘어가는 게 도움이 된다.

객체 지향 프로그래밍의 특징

상속 Inheritance

부모 클래스의 멤버를 자식 클래스가 상속받아 사용할 수 있으며, 이를 통해 코드의 재사용성이 높아지고 유지보수도 편리해진다

단, 부모 클래스의 private 멤버는 상속받을 수 없으며, protected 멤버는 자식 클래스에서 접근할 수 있다 상속은 extends 키워드를 사용하여 구현한다.

자식 클래스는 부모 클래스의 메서드나 변수를 그대로 쓸 수도 있고, 필요하면 재정의(오버라이딩)할 수도 있다

오버로딩 vs 오버라이딩

메서드 오버로딩 Method Overloading

같은 이름의 메서드를 매개변수의 개수나 타입을 달리하여 중복 정의하는 것

메서드 이름은 동일하지만, 인자의 개수나 타입이 다르다

이는 컴파일 시간에 결정되며, 코드의 가독성과 유연성을 높여준다.

class Calculator {
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }
}

메서드 오버라이딩 Method Overriding

상속 관계에서 부모 클래스의 메서드를 자식 클래스에서 재정의하는 것

부모 메서드와 동일한 이름, 리턴 타입, 매개변수를 가져야 하며, 이를 통해 자식 클래스에서 부모의 동작을 변경할 수 있다

이 동작은 런타임에 결정된다

class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

추상클래스와 인터페이스

추상 클래스와 인터페이스는 객체지향 프로그래밍에서 상속과 오버라이딩을 활용할 때 많이 등장하는 개념이다

추상 클래스 Abstract Class

하나 이상의 추상 메서드(구현되지 않은 메서드)를 포함하는 클래스

추상 클래스 자체로는 인스턴스를 만들 수 없다

따라서, 이를 상속받은 자식 클래스에서 모든 추상 메서드를 반드시 오버라이딩(구현)해야 한다

즉, 추상 클래스는 기본 설계도만 제공하고, 세부 구현은 자식 클래스에서 하도록 강제하는 구조다.

인터페이스 Interface

모든 메서드가 추상 메서드인 클래스

인터페이스는 구현체가 없기 때문에 인스턴스를 생성할 수 없으며, 클래스는 extends로 상속하고 인터페이스는 implements를 사용하여 구현한다

클래스는 하나만 상속할 수 있지만, 인터페이스는 여러 개를 구현할 수 있어서 다양한 기능을 한 번에 추가할 때 유용하다. 인터페이스를 구현한 클래스는 반드시 그 안의 모든 메서드를 구현해야 한다

이 두 개념을 이해하면, 오버라이딩이 왜 중요한지 더 쉽게 파악할 수 있다

특히, 추상 클래스나 인터페이스에서 정의된 메서드들을 재정의하는 방식으로 자식 클래스에서 자신만의 기능을 추가하거나 변경할 수 있기 때문이다.

배포를 위해 SSL인증과 DNS설정을 하고 있었다

A, CNAME 레코드를 추가하라는데

도통 이게 무슨 소린지 모르는채로 넘어갈 수가 없어 알아보았다

DNS가 뭐임?

인터넷에 연결된 컴퓨터 한대를 host 호스트라고 한다

호스트와 호스트가 통신하기 위해서는 주소가 필요하고, 이를 위해 IP 주소를 사용한다

하지만 매번 숫자로 된 IP주소를 외우기엔 불편해 만들어진 것이 도메인 네임 시스템 DNS이다

 

DNS의 핵심은 DNS Server인데, 수많은 도메인 이름과 그에 대응하는 IP 주소가 저장된 분산된 데이터베이스이다

우리 컴퓨터에서 www.어떤도메인.org 를 요청할 때, 운영체제는 DNS 서버에서 www.어떤도메인.org 의 ip 주소를 요청하여 받아온다

그 ip 주소를 이용해 우리가 원하는 도메인으로 접속하게 된다

hosts 파일

사실 DNS 서버를 거치지 않고도 hosts 파일을 통해 자주 가는 사이트의 IP 주소를 저장해둘수도 있다 (전화번호부처럼)

그러나 hosts 파일을 해킹 당해 수정되버리면 피싱 사이트로 유도될 위험이 있기 때문에 보안에 매우 유의해야 한다

public DNS, ISP

그렇다면 우리 컴퓨터는 DNS 서버의 위치를 어떻게 알고 요청을 하는가?

그건 바로 우리가 가입한 통신사 (KT, SK, LG U+)에서 기본적으로 제공하는 DNS 서버를 사용하기 때문이다

이런 통신사를 ISP Internet Service Provider 라고 한다

그런데 한가지 상상의 나래를 펼치자면 웹 서핑을 하려면 ISP에서 제공하는 DNS 서버를 사용해야하고, 그렇다면 ISP에서 내가 접속한 사이트를 다 알수도 있지 않을까?!

아무튼 모종의 이유로 통신사에서 제공하는 DNS server를 이용하고 싶지 않을 땐, public DNS를 사용해보자

구글이 만든 8.8.8.8 public DNS도 있고, 1.1.1.1로 쌈박한 주소를 가지고 있는 Cloudflare의 public DNS도 있다 (1.1.1.1은 자기네가 구글이나 보통의 ISP보다 속도도 빠르다고 어필한다)

도메인 이름의 구조

도메인의 끝에는 사실 . 이 생략되어 있다

점.은 루트를 의미하고 그 앞으로 탑 레벨 Top Level, 세컨드 레벨 Second Level, 서브 레벨 sub Level 로 이루어져있다

그리고 각각의 레벨마다 해당 도메인 이름에 대응하는 IP 주소를 저장한 DNS 서버가 존재한다

루트 DNS 서버는 Top-level DNS 서버의 목록을 알고있고, Top-level DNS 는 Second-level DNS의 목록을 알고있고, 각 하위 계층의 목록들만 알고 있다
결국 서브 도메인에 접속하려는 사이트의 IP주소가 있는 것이다

 

우리의 컴퓨터는 수 많은 서브 DNS 서버의 주소를 저장하기 어렵기에 루트 DNS 서버의 주소만 알고있다

 

그래서 루트 계층에서 탑으로, 탑에서 세컨으로, 세컨에서 서브로 순차적으로 각 레벨의 DNS 서버에 접근하여 최종적으로 해당 IP 주소를 얻어내는 방식으로 통신한다

도메인 이름 등록 과정과 원리

전세계 Root name server는 ICANN 이라는 비영리 단체에서 관리한다

Top-level domain 들은 Registry 등록소라는 기관에서 담당한다

 

도메인을 등록하려면 탑레벨 도메인에 주소를 직접 등록 할 수 없고, 네임 서버 형식으로 등록해야 하므로 네임서버가 하나 필요하다

 

등록대행자를 통해 네임서버를 탑레벨 도메인에게 전달해 네임서버를 등록하고, 네임서버에 내 도메인의 IP주소를 등록하면 각각의 서버가 하위 서버를 알고 있기때문에 이제 루트 서버부터 내 IP주소를 찾아 접속이 가능해진다

 

위의 서버 이미지 밑에 각각 저장된 도메인 정보들이 있는데, 이렇게 서버에 저장되어 있는 데이터 형식을 레코드 타입이라고 한다

여기서 A 와 NS는 최종 IP주소 = Address를 뜻하는 A레코드 와 네임서버를 뜻하는 NS레코드를 뜻한다

CNAME은 또 뭔데?

A 레코드는 도메인 이름을 정확한 IP 주소에 매핑한다

CNAME 레코드는 도메인 이름을 다른 도메인 이름에 매핑하여, 간접적으로 IP 주소에 연결한다

특정 도메인이 자주 변경될 때 유용하다

예를 들어, A 레코드를 직접 수정하지 않고도 CNAME 레코드를 수정함으로써 도메인의 별칭을 통해 IP 주소가 변경되더라도 서비스 접속에 문제가 생기지 않도록 할 수 있다

즉, A 레코드가 직접 IP 주소를 가리킨다면, CNAME 레코드는 별칭처럼 다른 도메인을 가리키는 방식이다

Ref : https://youtube.com/playlist?list=PLuHgQVnccGMCI75J-rC8yZSVGZq3gYsFp&si=4ve-6cyJ3wZyV7Qu

'Backend Basics > Cloud & Deployment' 카테고리의 다른 글

로드 밸런서가 뭐임?  (0) 2024.10.08

 

 

로드 밸런서와 로드 밸런싱

서버와 클라이언트를 각각 Express와 React(TypeScript)로 나누어 개발했다

배포할 때 AWS Elastic Beanstalk을 사용하면서 로드 밸런서라는 개념을 처음 접했다

백엔드 지식이 부족해서 하나하나 설정하는 과정이 생소했는데, 정보처리기사 시험을 준비하면서 로드 밸런싱 개념이 다시 등장해 글로 정리해본다

로드 밸런싱 (Load Balancing)이란?

로드 밸런싱은 네트워크 트래픽을 여러 서버에 분배해, 한 서버에 과도한 부하가 걸리지 않도록 해주는 기술이다 이렇게 트래픽을 분산하면 특정 서버가 과부하로 인해 다운되는 상황을 방지할 수 있다

또한, 로드 밸런싱 덕분에 서버를 쉽게 추가하거나 제거할 수 있고, 트래픽의 변화에 유연하게 대응할 수 있어 확장성 측면에서 매우 유리하다

로드 밸런서 (Load Balancer)의 역할

  • 여러 대의 서버가 있을 경우, 이들 사이에 네트워크 요청을 효율적으로 분배하는 장치

만약 서버 중 하나가 다운되거나 문제가 발생하면, 로드 밸런서는 자동으로 트래픽을 다른 정상적인 서버로 리다이렉트해서 서비스의 지속성을 보장한다

로드 밸런싱의 분배 방식

  • Random (랜덤 분배): 요청을 무작위로 서버에 할당
  • Least Loaded (가장 적은 부하 서버 선택): 현재 가장 적은 양의 작업을 처리하고 있는 서버에게 요청 할당
  • Least Connection (최소 연결 서버 선택): 현재 연결된 클라이언트 수가 가장 적은 서버에 요청 할당 서버의 성능이 비슷하게 구성돼 있을 때 가장 효과적으로 트래픽을 분산할 수 있다
  • Round Robin (라운드 로빈): 순차적으로 작업을 분배해 빠르지만, 서버의 성능 차이를 고려하지 않는다
    • Weighted Round Robin (가중치 라운드 로빈): 서버의 성능 차이를 반영해, 더 성능 좋은 서버에 더 많은 요청 할당
  • Source IP Hash (소스 IP 해시): 클라이언트의 IP 주소를 해시한 결과를 기반으로 특정 서버에 요청을 보내며, 클라이언트가 항상 같은 서버에 연결될 수 있도록 한다

Ref : https://guide-fin.ncloud-docs.com/docs/networking-loadbalancer-loadbalanceroverview

'Backend Basics > Cloud & Deployment' 카테고리의 다른 글

DNS Domain Name System 가 뭐임  (2) 2024.10.09

C언어로 된 파일을 만들 때 항상 맨 윗줄에 추가하게 되는

#include <stdio.h>

인클루드는 소스코드에 사용하기 위해 불러오는 말이라는걸 알고 있었고, 왠지 스펠링상 스튜디오 h 라고 무의식중에 읽고 있었는데

stdio는 알고보니 Standard Input/Output library header 였다

표준입출력 라이브러리를 가져와 쓴다는 의미이며 h는 헤더임을 뜻한다

+ Recent posts