[Software Development] 모바일 애플리케이션 아키텍처 vs. 디자인 패턴

원문 : [MEDIUM] Mobile Application Architecture vs. Design Patterns

모바일 애플리케이션 아키텍처: 기반 체계 설계

  • 정의: 애플리케이션의 전체 구조 및 조직을 설계하는 청사진. 고수준의 구성요소, 그들 간의 상호작용 및 데이터 흐름을 정의.
  • 인기 있는 아키텍처 패턴:
    • MVC (Model-View-Controller): 데이터 및 비즈니스 로직(Model), 프레젠테이션 및 UI 관리(View), 사용자 입력에 따른 Model 및 View 업데이트(Controller).
    • MVVM (Model-View-ViewModel): MVC의 진화형, 비즈니스 로직과 UI 분리.
    • Clean Architecture: 관심사의 분리와 의존성 역전을 강조.
    • Redux Architecture: 중앙 집중식 상태 관리와 단방향 데이터 흐름 제공.
  • 핵심 고려사항: 애플리케이션의 전반적인 구조를 결정하며, 확장성 및 유지보수성을 용이하게 함.

디자인 패턴: 특정 문제 해결을 위한 솔루션

  • 재사용 가능한 솔루션: 개발 과정 중 흔히 마주치는 문제에 대한 베스트 프랙티스 제공.
  • 자주 사용되는 디자인 패턴:
    • Singleton Pattern: 클래스의 인스턴스가 단 하나만 존재하도록 보장.
    • Observer Pattern: 객체 간의 일대다 의존성 설정, 상태 변경 시 의존 객체에 알림.
    • Adapter Pattern: 기존 클래스의 인터페이스를 다른 인터페이스로 사용 가능하게 함.
    • Decorator Pattern: 객체에 동적으로 추가 책임을 부여.
  • 세부적 문제 해결: 아키텍처 내 특정 문제에 대한 유연한 솔루션 제공.

아키텍처 vs. 디자인 패턴: 격차 해소

  • 범위 및 규모: 아키텍처는 전체 애플리케이션의 구조를, 디자인 패턴은 특정 문제를 해결.
  • 추상화 수준: 아키텍처는 고수준의 구성요소와 그 상호작용을, 디자인 패턴은 세부적인 코딩 문제를 다룸.
  • 유연성 및 적응성: 아키텍처는 앱의 기반을 설정하고, 디자인 패턴은 유연성을 제공하여 변화에 쉽게 적응.

핵심 요약

  • 모바일 애플리케이션 개발에서 성공은 견고한 아키텍처와 잘 적용된 디자인 패턴의 조화로운 통합에서 비롯된다. 아키텍처는 전체 프로젝트 구조를 결정하고, 디자인 패턴은 특정 도전 과제를 더 세부적인 수준에서 해결하는 정밀 도구를 제공한다. 이 두 기둥 간의 시너지는 개발자가 모바일 앱 개발의 복잡성을 탐색하게 하며, 시각적으로 매력적일 뿐만 아니라 장기적으로 확장 가능하고 유지보수가 용이한 애플리케이션을 창출할 수 있게 한다.

용어 정리

  • MVC (Model-View-Controller): 데이터(Model), 사용자 인터페이스(View), 사용자 입력 처리(Controller)로 구성된 소프트웨어 디자인 패턴.
  • MVVM (Model-View-ViewModel): MVC를 발전시킨 패턴으로, ViewModel을 통해 View와 Model 사이의 의존성을 줄임.
  • Clean Architecture: 소프트웨어 설계의 관심사 분리를 강조하는 아키텍처 패턴.
  • Redux Architecture: 애플리케이션 상태를 관리하기 위한 예측 가능한 상태 컨테이너.
카테고리: Article 정리, Software Development | 댓글 남기기

[Software Development] 기술 부채를 다루는 방법

원문 : [MEDIUM] How To Deal With Technical Debt

기술 부채 대처 방법

  • 기술 부채는 개발 팀에 큰 불만과 소진을 초래할 수 있으며, 소프트웨어 엔지니어들은 이의 부정적인 영향을 인지하고 있지만, 개발 과정에서 빠르고 쉬운 솔루션을 선택하는 것의 위험성을 제품 팀에 설명할  필요가 있다.
  • 워드 커닝햄이 1992년에 기술 부채라는 용어를 처음 사용했으며, 마틴 파울러는 기술 부채 생성의 네 가지 경로를 설명했지만, 특히 스타트업 회사에서 품질보다 속도를 우선시하면서 더 많은 기술 부채가 발생한다.
  • 개발자 생산성에 대한 연구에서는 회사가 단기 목표를 위해 품질을 희생하면서 새로운 기술 부채를 도입해야 하는 상황이 자주 발생한다고 한다. 기술 부채는 조직 전체뿐만 아니라 개발자의 행복, 직업 만족도, 그리고 사기에도 영향을 미친다.
  • 기술 부채의 주요 문제는 코드가 추상적인 개념이어서 비즈니스와 경영진에게 무슨 일이 발생하는지 설명하기 어렵다는 것이다.
  • 기술 부채로 인해 새로운 기능 개발 능력이 감소하고, 시스템의 복잡성이 쌓이면서 개발 사이클당 새 기능에 대한 가능성이 감소한다.

기술 부채의 유형

  • 나쁜 코드, 테스팅 부족, 모듈 간의 결합, 구식 라이브러리나 도구 사용, 수동 프로세스, 부적절한 아키텍처, 문서화 부족, 지식 공유 부족 등 다양한 유형의 기술 부채가 있다.

기술 부채와 싸우기 위한 전략

  • 기술 부채를 우선시하고, 개발 프로세스와 코드베이스를 분석하여 병목 현상을 찾아내고, 기술 부채 맵을 생성한 후, 해당 부분을 식별하여 우선 순위를 정한다.
  • 개발, 병목 현상, 속도 향상 필요성에 대해 투명하게 하고, 모든 이해 관계자가 동일한 이해를 공유할 수 있는 언어로 요약한다.
  • 시스템에 대한 명확한 소유권과 책임을 설정하고, 기술 부채 해결을 제품 개발의 자연스러운 흐름에 포함시켜 팀이 문제를 해결할 수 있도록 한다.

핵심 요약

  • 기술 부채는 개발 과정에서 불가피하게 발생하지만, 새로운 부채의 생성을 줄이고 기존 부채를 줄이는 전략이 필요하다.
  • 다양한 유형의 기술 부채를 인식하고, 이를 관리 및 감소하기 위한 프로세스와 전략이 많은 조직에서 부족하다.
  • 기술 부채를 우선 순위에 따라 해결하고, 투명성을 유지하며, 시스템에 대한 명확한 소유권을 확립하고, 팀이 기술 부채를 해결할 수 있도록 하는 것이 중요하다.

용어 정리

  • 기술 부채(Technical Debt): 소프트웨어 개발 과정에서 품질을 희생하여 단기 목표를 달성하기 위해 선택한 설계나 코드상의 불완전함으로, 장기적으로 추가 작업이 필요한 상태.
  • 코드 품질(Code Quality): 코드가 이해하기 쉽고, 유지보수가 용이하며, 오류가 적은 정도를 나타내는 지표.
  • 아키텍처(Architecture): 소프트웨어 시스템의 전체적인 구조와 구성요소, 그 구성요소간의 관계 등을 설계하는 과정 또는 그 결과물.
카테고리: Article 정리, Software Development | 댓글 남기기

[Software Development] 스프링 부트에서 데이터 전송 객체 (DTO)

원문 : [MEDIUM] Data Transfer Object (DTO) in Spring Boot

1. Data Transfer Object (DTO)란?

  • DTO는 애플리케이션의 다양한 계층 간 데이터를 캡슐화하여 전송하는 디자인 패턴이다. DTO는 필요한 필드만 포함하고 비즈니스 로직은 포함하지 않는 경량 객체다.

2. Spring Boot에서 DTO 사용의 이점

  • 데이터 분리: 내부 도메인 모델과 외부 표현을 분리해 데이터 전송을 관리한다.
  • 오버헤드 감소: 특정 사용 사례에 필요한 필드만 포함시켜 네트워크를 통한 데이터 전송량을 줄인다.
  • 버전 관리 및 호환성: 도메인 모델과 별개로 DTO를 발전시켜 API 변경을 쉽게 관리한다.
  • 보안 강화: 민감한 정보의 노출을 피하고 데이터 접근을 제한한다.
  • 테스팅 용이성: 복잡한 도메인 객체에 의존하지 않고 테스트 시나리오에서 DTO를 쉽게 생성하고 조작할 수 있다.

3. Spring Boot에서 DTO 사용 방법

3.1. 수동 DTO 생성

  • 도메인 엔티티의 구조를 반영하는 DTO 클래스를 직접 생성하고, 도메인 객체와 DTO 간 데이터를 매핑한다.

3.2. ModelMapper 사용

  • ModelMapper 라이브러리를 사용하여 도메인 객체와 DTO 간의 매핑을 자동화한다.

3.3. Lombok 사용

  • Lombok 라이브러리를 사용하여 DTO 클래스 생성을 간소화한다.

4. DTO에서 다양한 값 유형 포맷팅

  • 날짜 및 시간(@JsonFormat), 숫자(@NumberFormat), 문자열 및 Enum, 불리언 값 등의 포맷팅 방법을 제공한다.

5. 추가 고려 사항 및 모범 사례

  • DTO 검증: Spring의 검증 어노테이션(@NotNull, @Size 등)을 사용하여 DTO 필드를 검증한다.
  • 복잡한 중첩 객체에 대한 DTO: 중첩된 객체 또는 관계를 정확히 표현하기 위해 중첩된 DTO를 생성할 수 있다.
  • DTO 버전 관리: 애플리케이션의 진화에 따라 DTO를 버전 관리하여 후방 호환성을 유지한다.
  • RESTful API에서의 DTO 사용: 특정 사용 사례와 클라이언트 요구 사항에 맞게 DTO를 선택하고 구조화한다.

6. Spring Validation을 사용한 DTO 검증

  • 컨트롤러 메서드에서 @Valid 어노테이션을 사용하여 DTO에 정의된 검증 제약 조건에 따라 검증을 자동으로 트리거한다.

7. 마이크로서비스 아키텍처에서의 DTO

  • 각 마이크로서비스는 특정 요구 사항에 맞춘 자체 DTO 세트를 가질 수 있으며, 이는 마이크로서비스 간의 느슨한 결합을 보장하고 독립적인 진화를 가능하게 한다.

8. 결론

  • DTO는 Spring Boot 애플리케이션에서 필수적이며, 데이터 분리, 오버헤드 감소, 보안 강화 및 테스팅 용이성 등의 이점을 제공한다. 수동 생성, ModelMapper, Lombok 등 다양한 방법을 통해 DTO를 효율적으로 관리할 수 있다.

핵심 요약

  • DTO는 애플리케이션의 다양한 계층 간 데이터 전송을 위한 디자인 패턴이다.
  • Spring Boot에서 DTO 사용은 데이터 분리, 오버헤드 감소, 보안 강화 등의 이점을 제공한다.
  • 수동 생성, ModelMapper, Lombok을 포함한 다양한 방법으로 DTO를 구현할 수 있다.

용어 정리

  • DTO(Data Transfer Object): 다른 계층 간 데이터 전송을 위해 사용되는 객체.
  • ModelMapper: 도메인 객체와 DTO 간의 매핑을 자동화하는 라이브러리.
  • Lombok: 반복적인 코드(예: getter, setter)를 줄이기 위한 자바 라이브러리.
카테고리: Article 정리, Server | 댓글 남기기

[Kotlin] 다형성과 인터페이스

원문 : [MEDIUM] Polymorphism and Interfaces in Kotlin: A Powerful Duo

다형성과 인터페이스

  • 다형성: 다양한 클래스의 객체를 공통 슈퍼클래스의 객체로 취급할 수 있게 하는 객체지향 프로그래밍의 원리입니다. 이를 통해 하나의 인터페이스나 슈퍼클래스로 여러 관련 클래스를 대표할 수 있습니다.
    • 예를 들어, Shape 인터페이스를 정의하고 각각의 도형(Circle, Square, Triangle)이 이를 구현하여 draw() 메소드를 각기 다른 방식으로 실현할 수 있습니다. 이를 통해 모든 도형을 Shape 인터페이스의 인스턴스로 취급하여 교체 가능하게 사용할 수 있습니다.
  • 인터페이스: 클래스가 구현해야 하는 메소드와 속성의 집합을 정의한 계약입니다. 다양한 클래스 간의 협업을 위한 청사진 역할을 합니다.
    • 인터페이스는 추상화와 디커플링을 달성하게 해, 모듈화되고 유지보수가 용이한 코드를 작성할 수 있게 합니다. 예를 들어, MusicPlayer 인터페이스를 정의하고, SpotifyPlayerAppleMusicPlayer 같은 여러 클래스가 이를 구현함으로써 다양한 음악 플레이어를 손쉽게 교체할 수 있습니다.

핵심 요약

  • 다형성을 통해 다양한 클래스의 객체를 하나의 인터페이스로 취급하여 코드의 유연성과 확장성을 높일 수 있습니다.
  • 인터페이스는 클래스가 구현해야 할 메소드와 속성을 정의함으로써, 다양한 구현체 간의 교체 가능성과 코드의 모듈성을 증가시킵니다.
  • Kotlin에서의 다형성과 인터페이스 사용은 코드의 재사용성과 유지보수성을 향상시키며, 소프트웨어 엔지니어링에서 지속적인 학습과 실험을 통해 더 나은 소프트웨어를 개발할 수 있게 합니다.

용어 정리

  • 다형성(Polymorphism): 하나의 인터페이스/슈퍼클래스로 다양한 객체를 참조할 수 있는 특성을 말합니다.
  • 인터페이스(Interface): 클래스가 구현해야 하는 메소드와 속성의 집합을 정의한 계약입니다. 추상화와 디커플링을 통해 코드의 모듈성을 향상시킵니다.
카테고리: Article 정리, Kotlin | 댓글 남기기

[Database] 데이터베이스 유형, 스케일링, 성능 최적화

원문 : [MEDIUM] System Design Interview: Mastering Databases

이 글은 시스템 디자인 인터뷰를 준비하는 이들을 대상으로 데이터베이스의 기본부터 세부 사항까지 다룹니다. 데이터베이스는 시스템 설계의 핵심으로, 데이터 저장, 검색, 조직화에 중요한 역할을 합니다. 여기서는 데이터베이스의 다양한 유형과 특징, 스케일링 기술, 성능 최적화 방법에 대해 설명합니다.

데이터베이스 유형

  1. 관계형 데이터베이스: PostgreSQL, MySQL, SQLite와 같이 테이블을 기본 데이터 저장 단위로 사용하며, SQL을 쿼리 언어로 사용합니다. 거래 처리, 복잡한 쿼리, 데이터 무결성 확보에 유리합니다.
  2. NoSQL 데이터베이스: MongoDB, Cassandra, Redis와 같이 고정된 스키마 없이 유연하게 사용할 수 있으며, 비구조화된 데이터 처리에 적합합니다.
  3. 인메모리 데이터베이스: Redis, Memcached와 같이 데이터를 메모리에 저장하여 빠른 데이터 접근을 가능하게 합니다. 주로 캐싱 및 세션 관련 데이터 저장에 사용됩니다.

ACID 속성

  • Atomicity(원자성): 모든 거래는 완전히 실행되거나 전혀 실행되지 않습니다.
  • Consistency(일관성): 거래 후 데이터베이스는 일관된 상태를 유지합니다.
  • Isolation(독립성): 각 거래는 독립적으로 작동합니다.
  • Durability(지속성): 데이터가 한 번 커밋되면 영구적으로 저장됩니다.

데이터베이스 스케일링

  • 수직 스케일링(스케일 업): 단일 서버의 성능을 향상시킵니다.
  • 수평 스케일링(스케일 아웃): 여러 대의 서버를 추가하여 리소스 풀을 확장합니다. 데이터 샤딩과 복제를 포함합니다.

데이터베이스 성능 향상

  • 캐싱: 자주 쿼리되는 데이터를 메모리에 저장하여 성능을 향상시킵니다.
  • 인덱싱: 자주 접근하는 컬럼에 대한 데이터베이스 인덱스를 사용하여 검색 시간을 단축합니다.
  • 쿼리 최적화: 쿼리를 간소화하고, 조인을 최소화하며, 일반적인 SELECT * 사용을 피합니다.

데이터베이스 설계에서의 보안 우선순위

  • 데이터는 저장 시와 전송 시 암호화되어야 합니다.
  • SQL 인젝션에 대비하여 준비된 문장을 사용해야 합니다.
  • 역할 기반 접근 제어를 구현하여 보안을 강화해야 합니다.

CAP 이론

  • 일관성(Consistency), 가용성(Availability), 분할 내성(Partition tolerance) 중 두 가지만 동시에 달성할 수 있다는 이론입니다. 애플리케이션의 요구 사항에 따라 선택해야 합니다.

이 글은 데이터베이스에 대한 이해가 시스템 디자인 인터뷰에서 중요한 한 조각임을 강조하며, 계속해서 탐구하고 학습할 것을 권장합니다.

핵심 요약

  • 데이터베이스는 시스템 설계의 핵심으로, 관계형, NoSQL, 인메모리 데이터베이스 등 다양한 유형이 있습니다.
  • ACID 속성은 데이터베이스의 효과적인 기능을 보장합니다.
  • 데이터베이스는 수직 또는 수평으로 스케일링할 수 있으며, 성능 최적화를 위해 캐싱, 인덱싱, 쿼리 최적화가 필요합니다.
  • 데이터베이스 설계에서 보안은 우선시되어야 하며, CAP 이론은 데이터베이스 결정에 있어 중요한 요소입니다.
카테고리: Article 정리, Database | 댓글 남기기

[Network] URI, URL 및 URN 정리

웹을 사용하거나 공부하다 보면 URI, URL 라는 개념을 자주 접하게 됩니다. 그리고 URI, URL 개념을 공부하다 보면 꼭 따라오는 것이 URN입니다. 이번 기회에 해당 개념들을 정리하고자 합니다.

출처

  • URI : https://datatracker.ietf.org/doc/html/rfc3986
  • URL : https://datatracker.ietf.org/doc/html/rfc1738
  • URN : https://datatracker.ietf.org/doc/html/rfc2141

🔍 1. URI (Uniform Resource Identifier)

  • 정의: 인터넷 자원을 식별하기 위한 문자열입니다.
  • 세부 내용: 웹 상의 자원의 위치나 이름 등을 나타냅니다. URI에는 크게 URL과 URN, 두 가지 하위 분류가 있습니다.

🌐 2. URL (Uniform Resource Locator)

  • 정의: 웹 상에서 자원이 어디에 위치하는지를 알려주는 주소입니다.
  • 세부 내용: URL은 특정 접근 프로토콜(예: http, https, ftp 등), 호스트 명, 포트 번호, 경로 등으로 구성됩니다.
  • 예시: http://www.example.com:80/path/to/resource

📖 3. URN (Uniform Resource Name)

  • 정의: 자원의 이름으로서, 그 위치와 무관하게 그 자원을 고유하게 식별해주는 식별자입니다.
  • 세부 내용: 현재는 널리 사용되지 않지만, ISBN (국제 표준 도서 번호) 같은 것이 URN의 예시로 들 수 있습니다.
  • 예시: urn:isbn:0451450523

🎨 그림 설명

[이곳에 그림 삽입]

  1. 하나의 큰 원을 그리고 그 안에 URI라고 쓴다.
  2. URI 원 안에 두 개의 작은 원을 그리고 각각 URLURN이라고 쓴다. 이를 통해 URL과 URN이 URI에 포함된다는 것을 시각적으로 표현한다.

🚀 파인만 테크닉으로 쉽게 알아보기

상상해볼까요? 세상에는 수많은 집들이 있어요. 그 집들을 찾기 위해서는 주소가 필요한데, 이 주소가 바로 URL이에요. 그런데 우리가 집 주소 외에도 다른 방법으로 집을 알아볼 수 있어요. 예를 들면, “철수네 집”이라는 이름으로도 알 수 있죠. 이런 다양한 방법으로 집을 가리키는 것을 URI라고 해요. URL은 그 중에서도 정확한 주소를 나타내는 방법이에요. 모든 URL은 URI지만, 모든 URI가 URL은 아니에요.

요약하면,

URL, URN은 URI의 하위 개념이다.

 


출처: Tim Berners-Lee et al. “Uniform Resource Identifier (URI): Generic Syntax”. STD 66, RFC 3986, January 2005.

카테고리: Web Development | 댓글 남기기

[Web Development] CSR (Client-Side Rendering)과 SSR (Server-Side Rendering) 이해하기

웹페이지를 브라우저에서 렌더링하는 방식에는 크게 두 가지, CSR (Client-Side Rendering)과 SSR (Server-Side Rendering)가 있습니다. 이 두 방식은 사용자에게 웹페이지를 어떻게 제공하는지에 대한 핵심적인 차이를 보입니다. 이 포스트에서는 CSR과 SSR의 개념, 작동 방식, 장단점을 통해 두 기술을 살펴보겠습니다.

해당 포스트에서 언급하는 렌더링은 웹 브라우저에서 HTML을 이용해 실제 사용자 인터페이스를 그리는 과정을 뜻하는 것이 아니며, 서버 혹은 클라이언트 단에서 템플릿 엔진이 HTML을 생성하는 것을 의미합니다.

CSR (Client-Side Rendering)

CSR은, 말 그대로 클라이언트 측에서 웹페이지를 렌더링하는 방식을 의미합니다. 사용자가 웹사이트에 요청을 보내면 서버는 HTML 파일과 JavaScript 파일을 클라이언트에게 보냅니다. 클라이언트의 브라우저는 이 파일을 사용하여 웹페이지를 구성하고 렌더링합니다.

CSR 작동 방식

출처 : https://www.solutelabs.com/blog/client-side-vs-server-side-rendering-what-to-choose-when

  1. 사용자가 웹사이트를 요청합니다.
  2. 이 요청은 CDN(Content Delivery Network)에 도달합니다.
  3. CDN은 HTML 파일과 JavaScript에 대한 링크를 클라이언트로 전송합니다. 클라이언트는 HTML을 먼저 다운로드하고 이후 JavaScript를 다운로드합니다. 이때, SSR과 달리 사용자는 아직 웹페이지를 볼 수 없습니다.
  4. 브라우저는 JavaScript를 다운로드 합니다.
  5. JavaScript 파일이 완전히 다운로드되면, 해당 JavaScript가 실행됩니다. 이는 필요한 데이터를 서버에 요청하는 API를 호출하게 됩니다. 이 단계에서 사용자는 화면에 placeholder(데이터가 로드될 공간)를 보게 됩니다.
  6. 이제 서버는 API 요청에 응답하여 필요한 데이터를 제공합니다.
  7. 클라이언트는 이 데이터를 받아 placeholder에 채워넣습니다. 이제 웹페이지는 완전히 로드되어 사용자와 상호작용이 가능해집니다.

CSR의 장점

  • 서버 부하 감소 : 모든 렌더링 작업이 클라이언트에서 이루어지므로 서버 부하가 감소합니다.
  • 사용자와의 상호작용에 좋음 : 브라우저에서 직접 렌더링을 수행하기 때문에 동적인 사용자 인터페이스를 쉽게 구현할 수 있습니다.

CSR의 단점

  • 초기 로딩 시간이 길 수 있음 : 모든 자원을 다운로드 받아야 하기 때문에 초기 로딩 시간이 길어질 수 있습니다.
  • SEO 문제 : 웹 크롤러가 JavaScript를 완전히 해석하지 못하는 경우 페이지의 내용을 제대로 인식하지 못할 수 있습니다. (이 내용을 다른 포스트에서 추가로 다룰 예정입니다.)

참고 : Google Web Fundamentals – JavaScript Start-up Optimization

SSR (Server-Side Rendering)

반면에, SSR은 서버에서 웹페이지를 렌더링하는 방식입니다. 사용자가 웹사이트에 접속 요청을 보내면, 서버는 해당 요청에 맞는 HTML을 렌더링하고 이를 클라이언트에게 보냅니다. 클라이언트는 받은 HTML을 그대로 화면에 표시합니다.

SSR 작동 방식

출처 : https://www.solutelabs.com/blog/client-side-vs-server-side-rendering-what-to-choose-when

  1. 사용자가 웹사이트를 요청합니다.
  2. 이번에는 서버가 중요한 역할을 합니다. 서버는 ‘렌더링 준비가 된’ HTML 파일을 생성합니다. 이는 서버가 모든 리소스를 확인하고 필요한 HTML 컨텐츠를 컴파일하여 만들내는 과정을 말합니다.
  3. 클라이언트에게 전송되는 HTML은 이미 렌더링이 준비된 상태이므로, 클라이언트는 이를 즉시 화면에 렌더링합니다. 그러나 이 시점에서는 JavaScript가 아직 로드되지 않았으므로, 사이트는 아직 사용자 조작이 불가능합니다.
  4. 클라이언트는 이제 JavaScript를 다운로드합니다.
  5. JavaScript가 다운로드되는 동안, 사용자는 컨텐츠는 볼 수 있지만 사이트를 조작할 수는 없습니다. 이때 사용자의 조작은 저장되어 있다가 이후에 처리됩니다.
  6. 브라우저는 다운로드된 JavaScript를 실행합니다.
  7. 이제 JavaScript도 성공적으로 컴파일되었으므로, 이전에 저장된 사용자 조작이 실행됩니다. 웹 페이지는 이제 완전히 상호작용 가능한 상태가 됩니다.

SSR의 장점

  • 초기 로딩 시간이 짧음 : 필요한 HTML만 받아서 화면에 표시하면 되므로 초기 로딩 시간이 짧습니다.
  • SEO 향상 : 웹 크롤러가 HTML을 바로 인식할 수 있어서 검색 엔진 최적화(SEO)에 유리합니다.

SSR의 단점

  • 서버 부하 증가 : 모든 요청마다 새로운 HTML을 렌더링해야 하므로 서버 부하가 증가할 수 있습니다.
  • 사용자와의 상호작용에 제약: 모든 페이지 변경 요청마다 서버에 요청을 보내고 응답을 기다려야 하므로 사용자 경험에 제약이 생길 수 있습니다.

참고 : Google Search Central – Understand the JavaScript SEO basics

카테고리: Web Development | 댓글 남기기

[Web Development] CORS (Cross-Origin Resource Sharing) 이해하기

CORS 개요

CORS는 Cross-Origin Resource Sharing의 약자로, 다른 출처(origin)에서 자원을 공유할 수 있도록 하는 메커니즘입니다. 웹 페이지가 다른 도메인의 자원에 접근하려고 할 때, 보안상의 이유로 브라우저는 동일 출처 정책(Same-Origin Policy)을 적용합니다. 이 정책은 웹 페이지가 자신과 동일한 출처에서만 리소스를 로드하도록 제한합니다. 그러나 CORS를 사용하면 웹 애플리케이션은 이 제한을 우회하고 다른 도메인의 리소스에 접근할 수 있습니다.

CORS 작동 방식

CORS는 HTTP 헤더를 사용하여 도메인 간에 자원을 공유할 수 있도록 합니다. 웹 애플리케이션은 HTTP 요청을 보낼 때 ‘Origin’ 헤더를 포함하여 요청을 보냅니다. 이 헤더는 요청이 시작된 웹 페이지의 출처를 나타냅니다. 서버는 이 헤더를 확인하고, ‘Access-Control-Allow-Origin’ 헤더를 응답에 포함하여 해당 출처에서의 요청을 허용할지 결정합니다.

CORS 작동 방식

CORS의 중요성

CORS는 웹의 중요한 보안 메커니즘입니다. 웹 애플리케이션은 종종 다른 도메인의 API를 사용하여 데이터를 가져오거나, CDN(Content Delivery Network)을 통해 이미지나 스크립트를 로드합니다. 이러한 도메인 간의 자원 공유는 웹의 핵심 기능 중 하나이지만, 동일 출처 정책 없이는 보안 위협이 될 수 있습니다.

CORS는 어떤 리소스가 허용되고 어떤 리소스가 차단되는지를 결정하는 규칙을 제공합니다. 이 규칙은 웹 애플리케이션의 HTTP 응답 헤더에 의해 설정됩니다.

예를 들어, 웹 애플리케이션 A가 다른 도메인의 리소스에 접근하려고 할 때, 그 도메인의 서버는 ‘Access-Control-Allow-Origin‘ 헤더를 사용하여 웹 애플리케이션 A의 접근을 허용할 수 있습니다. 이 헤더가 없거나 웹 애플리케이션 A의 출처를 허용하지 않는 값이 설정되어 있으면, 브라우저는 리소스에 대한 접근을 차단합니다.

동일 출처 정책(Same-Origin Policy)이 없다면, 웹 애플리케이션은 다음과 같은 보안 위협에 노출될 수 있습니다.

  • 사이트 간 스크립팅 공격 (Cross-Site Scripting, XSS): 악의적인 스크립트가 웹 애플리케이션에 삽입되어 사용자의 데이터를 탈취하거나 조작하는 공격입니다. 동일 출처 정책이 없다면, 악의적인 스크립트가 다른 도메인에서 로드되어 실행될 수 있습니다.
  • 사이트 간 요청 위조 공격 (Cross-Site Request Forgery, CSRF): 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행동을 하도록 만드는 공격입니다. 동일 출처 정책이 없다면, 웹 애플리케이션은 다른 도메인의 요청을 받아들일 수 있으므로 CSRF 공격에 취약해집니다.

CORS(Cross-Origin Resource Sharing)는 이러한 보안 위협을 방지하면서도 필요한 경우 다른 도메인의 자원을 안전하게 사용할 수 있도록 하는 메커니즘입니다. CORS는 브라우저가 서버에게 자원을 요청할 때 추가적인 HTTP 헤더를 사용하여, 서버가 다른 도메인의 자원 요청을 허용하는지를 확인합니다.

CORS 스펙 문서

CORS에 대한 자세한 사항은 W3C의 공식 스펙 문서를 참조하시면 됩니다. 이 문서는 CORS의 작동 방식, HTTP 헤더의 사용 방법, 에러 처리 방법 등에 대한 자세한 정보를 제공합니다.

WC3 : Cross-Origin Resource Sharing

동일 출처(origin) 확인

CORS에서 동일 출처(origin)를 판단할 때, scheme, host, port 요소가 모두 동일한지 검사를 합니다. 이 중 한가지 라도 다르다면 동일한 출처(origin)으로 판단하지 않습니다.

Understanding "same-site" and "same-origin"

The Web Origin Concept – Origin of a URI

WebSocket을 활용한 실시간 채팅 앱을 구현하면서, 해당 개념을 깊게 이해하지 못해 애를 먹었는데요. 로컬에서 정상 동작하던 채팅 앱을 서버에 배포하면서, JavaScript 단에서 SockJS 생성시에 403 Forbidden 응답 코드가 리턴되었습니다.

결과적으로 서버단에서 registerStompEndpoints 메서드에 EndPoint를 추가하는 코드에 .setAllowedOrigins으로 특정 도메인을 허용하면서 문제를 해결하였습니다. (환경 변수 HOST_ENDPOINT를 통해 도메인을 참조합니다.)

예를 들어 https://chat.example.com 도메인으로 요청하는 경우, JavaScript 단에서 SockJS 생성시에 http 혹은 https에 따라, ws 혹은 wss 프로토콜로 업그레이드(wss://chat.example.com)를 합니다.

이런 경우에, scheme(프로토콜)이 변경되어 CORS에 의해 동일 출처(origin)가 아닌 것으로 판단되기 때문에 403 Forbidden 응답 코드가 리턴되었던 것입니다.

카테고리: Web Development | 댓글 남기기

[C] qsort() 함수

C언어에서 배열을 정렬하는 방법 중 하나는 표준 라이브러리 함수인 qsort()를 사용하는 것입니다. qsort()는 C 언어의 표준 라이브러리 중 하나인 stdlib.h 헤더 파일에 정의되어 있으며, 퀵 정렬 알고리즘을 기반으로 합니다. 이 함수는 배열의 요소 타입에 상관없이 정렬을 수행할 수 있습니다.

qsort() 함수의 원형은 다음과 같습니다.

여기서 ptr는 정렬할 배열을 가리키는 포인터, count는 배열에 있는 요소의 개수, size는 배열의 각 요소의 크기를 바이트 단위로 나타내며, comp는 비교 함수를 가리키는 포인터입니다.

다음은 qsort() 함수를 사용하여 정수 배열을 오름차순으로 정렬하는 예제입니다.

이 코드는 compare()라는 비교 함수를 사용하여 qsort() 함수를 호출합니다. compare() 함수는 두 개의 void 포인터를 인자로 받아, 이를 int 포인터로 형 변환한 후에 참조하여 두 정수를 비교합니다. 이 함수는 첫 번째 정수가 두 번째 정수보다 작을 경우 음수를, 같을 경우 0을, 큰 경우 양수를 반환합니다. 이렇게 하면 qsort() 함수는 이 비교 함수를 사용하여 배열을 오름차순으로 정렬할 수 있습니다.

그런데 qsort()는 정수 배열뿐만 아니라 문자열 배열도 정렬할 수 있습니다. 다음은 qsort() 함수를 사용하여 문자열 배열을 알파벳 순으로 정렬하는 예제입니다.

이 코드는 compare_strings()라는 비교 함수를 사용하여 qsort() 함수를 호출합니다. compare_strings() 함수는 두 개의 void 포인터를 인자로 받아, 이를 문자열 포인터로 형 변환한 후에 참조하여 두 문자열을 비교합니다. 이 함수는 첫 번째 문자열이 두 번째 문자열보다 사전 순으로 앞설 경우 음수를, 같을 경우 0을, 뒤설 경우 양수를 반환합니다. 이렇게 하면 qsort() 함수는 이 비교 함수를 사용하여 배열을 알파벳 순으로 정렬할 수 있습니다.

카테고리: C, Programming | 댓글 남기기

[Kotlin] Array와 IntArray의 차이점

Kotlin에서 정수 배열을 선언할 때, Array<Int>IntArray를 사용할 수 있습니다. 두 타입 모두 정수 배열을 나타내지만, 둘 사이에는 몇 가지 중요한 차이점이 있습니다.

1. 표현 방식

Array<Int>는 일반적인 배열로, 각 요소는 Int 객체입니다. 반면에 IntArray는 원시 타입 배열로, 각 요소는 원시 타입 int입니다.

예를 들어, 다음과 같이 각각 선언할 수 있습니다.

2. 메모리 사용량

Array<Int>는 각 요소가 객체이므로 IntArray보다 더 많은 메모리를 사용합니다. IntArray는 원시 타입을 사용하므로 메모리 효율성이 더 높습니다.

3. 성능

Array<Int>는 박싱(Boxing)과 언박싱(Unboxing) 과정이 필요하므로 IntArray보다 성능이 떨어질 수 있습니다. IntArray는 원시 타입을 사용하므로 이러한 과정이 필요 없습니다.

박싱(Boxing)이란 원시 타입의 값을 객체로 변환하는 과정을 의미합니다. 예를 들어, int 타입의 값을 Integer 객체로 변환하는 것이 박싱입니다. 반대로 언박싱(Unboxing)은 객체에서 원시 타입의 값을 추출하는 과정을 의미합니다.
이러한 박싱과 언박싱 과정은 추가적인 메모리와 CPU 시간을 소비하므로 성능에 영향을 줄 수 있습니다. 따라서 가능하다면 원시 타입을 사용하는 것이 성능 향상에 도움이 될 수 있습니다.

4. 결론

Array<Int>는 자바의 Integer[]와 호환되며, IntArray는 자바의 int[]와 호환됩니다.

따라서 성능과 메모리 효율성이 중요한 경우 IntArray를 사용하는 것이 좋습니다. 그러나 객체 지향적인 특성이나 일반적인 배열과의 호환성이 필요한 경우 Array<Int>를 사용하는 것이 좋습니다.

이러한 차이점을 이해하면 Kotlin에서 배열을 더 효과적으로 사용할 수 있습니다. 어떤 상황에서 어떤 타입의 배열을 사용할지 결정하는 데 이 정보가 도움이 되길 바랍니다.

카테고리: Kotlin, Programming | 댓글 남기기