728x90

https://console.firebase.google.com/u/0/

 

로그인 - Google 계정

이메일 또는 휴대전화

accounts.google.com

 

Firebase는 데이터가 변경될 때마다 클라이언트에 즉시 업데이트를 제공하여 실시간 애플리케이션에 유용하다.

다양한 인증 방법과 빠르고 안전한 웹 호스팅을 제공하며, SSL 인증서가 자동으로 제공되어 보안에도 좋다.

 

위 사이트에서 로그인을 한 뒤, 프로젝트를 생성하고 DB를 관리할 수 있는 Firestore에 접근해 api키를 볼 수 있다. 그리고 Firestore 규칙란에

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

이런식으로 내가 연결한 프로젝트의 전체 접근을 허용할 수 있고, 특정 유저만 접근 가능하게 권한설정도 가능하다.

 

firebase를 로컬환경으로 개발할땐, .env.local 파일에 firebase 관련 api키를 넣어놓는게 좋다.

 

아래는 리덕스툴킷을 사용한 firebase에서의 db삭제 예제이다.

import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { collection, getDocs, addDoc, updateDoc, deleteDoc, doc, query, orderBy, Timestamp } from 'firebase/firestore';
import { AppThunk } from './index';
import { db } from '../firebase/firebaseConfig';

...

deletePost(state, action: PayloadAction<string>) {
  state.posts = state.posts.filter(post => post.id !== action.payload);
}

...

export const removePost = (id: string): AppThunk => async (dispatch) => {
  try {
    const docRef = doc(db, 'posts', id);
    await deleteDoc(docRef);
    dispatch(deletePost(id));
  } catch (error) {
    console.error('Error deleting document: ', error);
  }
};

 

리덕스 툴킷을 사용하면 Date 객체를 상태나 액션에 저장하는 것이 매우 안좋다. 그래서 Date 객체를 직렬화 가능한 형태로 변환하여 저장하고 사용할 때는 다시 Date 객체로 변환해야한다.

interface BoardState {
  posts: { id: string; title: string; content: string, createdAt: string }[];
  loading: boolean;
  error: string | null;
}

...

addPost(state, action: PayloadAction<{ id: string; title: string; content: string; createdAt: string }>) {
  state.posts.unshift(action.payload);
},


...


  try {
    const q = query(collection(db, 'posts'), orderBy('createdAt', 'desc'));
    const querySnapshot = await getDocs(q);
    const posts = querySnapshot.docs.map((doc) => {
      const data = doc.data();
      return {
        id: doc.id,
        title: data.title,
        content: data.content,
        createdAt: (data.createdAt as Timestamp).toDate().toISOString() // Timestamp를 Date로 변환
      };
    });
    dispatch(fetchPostsSuccess(posts));
  }

 

app/duplicate-app오류는 firebase 앱을 초기화할 때 발생하는 오류이다. 동일한 앱이 이미 초기화되어 있는지 확인하고, 초기화되지 않은 경우에만 초기화하도록 해야한다.

 

@/firebase/firebaseConfig.ts

import { initializeApp, getApps, FirebaseApp } from 'firebase/app';
import { getFirestore, Firestore } from 'firebase/firestore';

interface FirebaseConfig {
  apiKey: string;
  authDomain: string;
  projectId: string;
  storageBucket: string;
  messagingSenderId: string;
  appId: string;
  measurementId?: string;
}

const firebaseConfig: FirebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY!,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN!,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID!,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET!,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID!,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID!,
  measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};

const apps: FirebaseApp[] = getApps();
let app: FirebaseApp;
if (apps.length === 0) {
  app = initializeApp(firebaseConfig);
} else {
  app = apps[0];
}

const db: Firestore = getFirestore(app);


export { db };

이미 초기화된 앱이 없다면 initializeApp을 호출하고, 있다면 그 앱을 재사용하게 해야 오류가 나지 않는다.

 

firebase 관리자는 db를 직접 추가, 수정, 삭제 등이 가능하다.

 

728x90
728x90

React에서 테스트 코드를 짤 때 API 핸들러 함수와 React 컴포넌트를 테스트하는 두 가지 주요 범주로 나눌 수 있다.

 

Next.js를 사용한다면 npm install node-mocks-http로 설치해서 사용하는 node.js 환경에서 http 요청 및 응답 객체를 모킹하는 라이브러리를 사용해도 좋다. 이 라이브러리를 사용하면, 테스트 시 실제 서버를 띄우지 않고도 http 요청과 응답을 시뮬레이션할 수 있다. 

 

Node.js와 Jest는 CommonJS 모듈을 사용하지만 ES 모듈을 사용하는 프로젝트에는 import { jest } from '@jest/globals';를 사용해보자 jest 객체를 전역으로 가져올 수 있고, require을 사용하는 CommonJS 환경에 맞춰진 Jest를 import를 사용하는 ES모듈에 사용할 수 있도록 도와준다.

 

루트폴더에서 __tests__라는 폴더를 만들고, 테스트대상인 파일명.test.js나 ts 로 이름을 짓고 테스트 코드를 작성해보자 대충 이런 느낌이다.

import { createMocks } from 'node-mocks-http';
import handler from './handler';
import { jest } from '@jest/globals';
import axios from 'axios';

jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

describe('API Handler', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });

  it('should return 200 with success message', async () => {
    const { req, res } = createMocks({
      method: 'GET',
    });

    await handler(req, res);

    expect(res._getStatusCode()).toBe(200);
    expect(res._getJSONData()).toEqual({ message: 'success' });
  });

  // 추가 테스트 케이스 작성
});

 

그리고 git commit이 될 때마다 test가 되어야하기 때문에 루트폴더/.github/workflows/ci.yml 파일을 만들어야한다.

name: Node.js CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [14.x, 16.x]

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}

    - name: Install dependencies
      run: npm install

    - name: Run tests
      run: npm test
      env:
        NEXT_PUBLIC_RIOT_API_KEY: ${{ secrets.NEXT_PUBLIC_RIOT_API_KEY }}

 

이렇게 코드를 짜면 env쪽에서 경고문구가 나올 수 있다. 이는 그냥 무시해도 좋은데 꼭 경고를 보기 싫으면 error lens와 같이 vscode extenstion을 고려할 수 있고 이유를 모르겠다 싶으면 settings.json에

"yaml.customTags": [
  "!<tag:yaml.org,2002:js/undefined> [0,1]",
  "!!binary sequence"
],
"yaml.schemas": {
  "https://json.schemastore.org/github-workflow.json": "file:///YOUR_WORKFLOW_FILE_PATH"
}

 이걸 추가해보자

 

민감정보가 담겨있는 환경변수들을 github에 몰래 올려야 하므로, github 저장소에서 Settings > Secrets and variables > Actions로 이동하여 New respository secret 버튼을 누른 뒤, 위 코드 기준 NEXT_PUBLIC_RIOT_API_KEY를 name에 입력하고 value에 api 키를 입력하는 식으로 등록을 해야한다.

 

그리고 프로젝트 루트에 jest.config.js를 생성하여 아래와 같이 코드를 쳐 Jest 설정을 해보자

module.exports = {
  testEnvironment: 'node',
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
  moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
  testMatch: ['**/*.test.ts', '**/*.test.tsx'],
};

 

이걸 github에 푸쉬하게 되면 

 

이렇게 초록색 체크를 볼 수있다. 만약 테스트에 실패했다면 위 사진에서 Details를 누르거나 Actions에 들어가서 테스트 실패한 이유도 확인할 수 있다.

취미로 만드는 서비스나 XP같이 빠르고 클라이언트의 요구사항 기능만 잘 됐으면 됐지 뭐 이런 마인드로 개발을 하고자하면 테스트 코드를 안짜는 것을 추천한다. commit 된 후에 배포된 도메인에서 확인하는 시간이 좀 더 길어질 뿐더러 테스트 코드 작성을 하며 시간적 리소스낭비로 개발 생산성도 떨어질 수 있기 때문이다.

 

저사양 노트북일때 github 테스트때메 chrome에서도 렉이 걸릴수도 있다. 그리고 test실패할 때마다 메일이 날아오므로 설정도 필요하다는 단점도 있다.

728x90

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

XSS, CORS, Code Injection  (0) 2024.05.26
host domain, origin, cookie, local, session  (0) 2024.05.05
HTTP, HTTPS, Mixed Content  (0) 2024.05.05
Web Workers와 Concurrency  (0) 2024.04.13
Synchronous I/O, Asynchronous I/O  (1) 2024.04.06
728x90

https://developer.riotgames.com/apis

 

Riot Developer Portal

 

developer.riotgames.com

 

위의 공식 게임회사의 제공된 API 사이트를 참고하여 특정 데이터를 가져올때, 프론트엔드에서 직접 Riot API를 호출하면 Riot에서 400대의 에러를 반환할 것이다. 보안상의 이유로 직접 프론트엔드에서 호출하는 것을 권장하지 않기 때문이다. API는 백엔드 서버를 통해 처리해야 한다.

 

하지만 백엔드 서버를 따로 작성하고 싶지 않을때, Next.js에서 지원하는 서버 사이드 렌더링을 통하여 프론트엔드와 백엔드 코드를 함께 작성할 수 있다.

import type { NextApiRequest, NextApiResponse } from 'next';
import axios from 'axios';

const RIOT_API_KEY = process.env.NEXT_PUBLIC_RIOT_API_KEY;
const ACCOUNT_REGION = 'kr';
const MATCH_REGION = 'asia';

const getSummonerByPUUID = async (puuid: string) => {
  const url = `https://${ACCOUNT_REGION}.api.riotgames.com/lol/summoner/v4/summoners/by-puuid/${puuid}`;
  return axios.get(url, {
    headers: {
      'X-Riot-Token': RIOT_API_KEY,
    },
  });
};

 

NextApiRequest와 NextApiResponse는 Next.js에서 API 라우트를 작성할 때 사용하는 타입이다. 이 타입들은 각각 HTTP 요청과 응답을 나타내며, Express.js와 같은 기존 Node.js 서버에서 사용하는 Request와 Response 객체와 유사한 역할을 한다. Next.js API 라우트에서 이 객체들을 사용하여 클라이언트의 요청을 처리하고 응답을 보낼 수 있다.

 

NextApiRequest는 클라이언트로부터 오는 HTTP 요청을 나타낸다. 이 객체에는 요청과 관련된 다양한 정보가 포함되어 있다.

  • req.query: URL 쿼리 문자열을 객체 형태로 포함한다. 예를 들어, URL이 /api/hello?name=John이라면 req.query는 { name: 'John' }이 된다.
  • req.body: 요청 본문을 포함한다. 주로 POST, PUT 요청에서 사용된다.
  • req.cookies: 요청에 포함된 쿠키를 객체 형태로 포함한다.
  • req.method: HTTP 요청 메서드를 나타낸다 (예: GET, POST, PUT, DELETE 등).
  • req.headers: 요청 헤더를 포함한다.

 

NextApiResponse는 서버에서 클라이언트로 보내는 HTTP 응답을 나타낸다. 이 객체를 사용하여 클라이언트에 데이터를 보내거나 상태 코드를 설정할 수 있다.

 

  • res.status(code: number): 응답 상태 코드를 설정한다. 예를 들어, res.status(200)은 성공 상태 코드를 설정한다.
  • res.json(data: any): JSON 형식의 데이터를 응답으로 보낸다.
  • res.send(data: any): 문자열이나 버퍼 데이터를 응답으로 보낸다.
  • res.setHeader(name: string, value: string | string[]): 응답 헤더를 설정한다.

 

적용 사례 url: (예시 입력 데이터: hide on bush # kr1)

https://www.theze.de/team-balance

 

https://www.theze.de/team-balance

 

www.theze.de

 

728x90
728x90

XSS(Cross-Site Scripting)는 웹 애플리케이션에서 발견되는 보안 취약점 중 하나다. XSS는 공격자가 웹 페이지에 악의적인 스크립트를 주입하고, 이 스크립트가 다른 사용자의 브라우저에서 실행되도록 함으로써 사용자의 정보를 탈취하거나 사기 행위를 저지를 수 있게 만드는 공격이다.

 

Stored XSS는 악의적인 스크립트가 웹 애플리케이션의 서버에 저장된다. 예를 들어, 게시판이나 댓글 섹션에 악의적인 스크립트가 포함된 내용을 작성할 수 있다. 사용자가 해당 페이지를 방문할 때마다 악의적인 스크립트가 실행된다. 이 유형은 다수의 사용자에게 영향을 미칠 수 있어 매우 위험하다.

 

Reflected XSS 사용자로부터 받은 입력이 즉시 실행된다. 예를 들어, 검색 결과 페이지에서 검색어를 그대로 브라우저에 출력할 때 악의적인 스크립트가 포함된 검색어를 사용자가 입력하면 스크립트가 실행된다. 이 스크립트는 주로 피싱 공격에 사용되며, 사용자가 링크를 클릭해야만 공격이 발동된다.

 

DOM-based XSS DOM(Document Object Model)의 취약점을 이용하여 공격이 수행된다. 웹 페이지의 클라이언트 사이드 스크립트가 DOM을 잘못 처리하여 발생한다. URL의 일부로 전달된 데이터가 안전하지 않게 처리되어 스크립트가 실행될 수 있다.

 

돔 기반 XSS 예시:

<!DOCTYPE html>
<html>
<head>
    <title>DOM-based XSS Example</title>
</head>
<body>
    <h1>Welcome</h1>
    <div id="message"></div>
    <script>
        var message = document.getElementById('message');
        var query = window.location.search.substring(1);
        message.innerHTML = decodeURIComponent(query);
    </script>
</body>
</html>

 

XSS는 사용자의 세션 토큰이나 쿠키를 탈취하여 사용자의 계정을 도용할 수 있다. 악의적인 스크립트를 통해 사용자의 브라우저에서 웹 사이트의 내용이나 기능을 변경할 수 있다. 가짜 로그인 폼을 생성하여 사용자의 로그인 정보를 탈취할 수 있다. 한 사용자의 브라우저에서 실행된 스크립트가 다른 사용자들에게 퍼질 수 있다.

 

입력 검증 및 새니타이징으로 사용자 입력을 검증하고, 스크립트 태그나 이벤트 핸들러 등을 포함할 수 있는 문자를 적절히 필터링하거나 이스케이프 처리를 해야한다.

 

콘텐츠 보안 정책(CSP)은 콘텐츠 보안 정책을 설정하여 외부 스크립트의 실행을 제한한다.

 

HTTPOnly 쿠키는 HTTPOnly 플래그를 쿠키에 설정하여 자바스크립트를 통한 쿠키 접근을 차단한다.

 

CORS(Cross-Origin Resource Sharing)는 웹 페이지가 다른 도메인의 리소스를 안전하게 요청할 수 있도록 하는 보안 메커니즘이다. 기본적으로 브라우저는 같은 출처 정책(Same-Origin Policy)을 적용하여, 스크립트가 실행 중인 원본(도메인, 프로토콜, 포트)과 다른 원본의 리소스에 대한 접근을 제한한다. CORS는 이 정책을 확장하여, 특정 조건 하에 다른 출처의 리소스 접근을 허용한다.

 

CORS는 HTTP 헤더를 사용하여 브라우저와 서버 간에 정보를 교환한다. 서버는 'Access-Control-Allow-Origin'과 같은 CORS 관련 헤더를 설정하여, 특정 출처의 브라우저가 해당 서버의 리소스에 접근할 수 있도록 허용한다.

 

메소드가 GET, HEAD, POST 중 하나이고, HTTP 헤더가 일반적으로 허용되는 헤더(CORS 사양에서 정의된 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type' 등)로만 구성되어 있을 때 단순 요청으로 간주된다.

 

이 경우 브라우저는 특별한 CORS 헤더 없이 요청을 보낸다. 서버는 응답에 'Access-Control-Allow-Origin' 헤더를 포함하여 요청을 허용할 출처를 지정한다.

 

PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH 또는 Content-Type이 application/json인 POST 등, 단순 요청에 해당하지 않는 메소드를 사용하는 요청은 사전 요청이 필요하다.

 

브라우저는 실제 요청을 보내기 전에 'OPTIONS' 메소드를 사용하여 사전 요청을 보낸다. 이 요청은 서버에게 실제 요청을 안전하게 처리할 수 있는지 확인하기 위한 것이다.

 

서버는 'Access-Control-Allow-Methods', 'Access-Control-Allow-Headers' 등의 헤더로 응답하여, 어떤 HTTP 메소드와 헤더, 출처가 허용되는지 명시한다.

 

쿠키나 HTTP 인증 정보와 같은 사용자 자격 증명을 포함하는 요청도 CORS 정책의 일부다. 이러한 요청을 허용하려면 서버는 'Access-Control-Allow-Origin' 헤더에 구체적인 출처를 명시하고, 'Access-Control-Allow-Credentials' 헤더를 'true'로 설정해야 한다.

 

CORS는 안전하지 않은 교차 출처 요청을 방지하면서도, 필요한 경우에는 이를 허용하여 웹 애플리케이션의 유연성을 증가시킨다.

 

API 제공자는 CORS를 통해 자신의 리소스에 대한 접근을 세밀하게 제어할 수 있다. 개발자는 서로 다른 도메인에서 호스팅되는 리소스를 안전하게 요청하고 활용할 수 있어, 서비스 구성의 유연성이 향상된다.

 

Code Injection은 공격자가 애플리케이션에 악의적인 코드를 주입하고 실행하게 만드는 보안 취약점을 이용한 공격이다. 이러한 공격은 일반적으로 애플리케이션의 입력 검증 또는 적절한 새니타이징이 부족할 때 발생한다. 공격자는 이 취약점을 통해 시스템을 제어하거나 데이터를 탈취하고, 추가적인 악의적 행위를 수행할 수 있다.

 

SQL Injection은 애플리케이션의 데이터베이스 쿼리에 악의적인 SQL 코드를 삽입하는 방식이다. 공격자는 데이터베이스에서 데이터를 읽어오거나 수정, 삭제하는 등의 작업을 수행할 수 있다.

 

서비스 거부(DoS)는 애플리케이션의 리소스를 고갈시켜 정상적인 서비스 제공을 방해할 수 있다.

728x90
728x90

Host는 네트워크에 연결된 장치를 지칭할 수도 있고, URL에서 웹 서버의 주소를 가리키는 용어이다. 웹 주소에서의 Host는 도메인 이름과 포트 번호를 포함할 수 있다. 예를 들어, URL http://www.example.com:80/page.html에서 Host는www.example.com:80이다. 여기서 www.example.com은 도메인 이름이고, 80은 포트 번호이다.

 

Domain은 인터넷에서 사이트를 식별하는 주소이다. 쿠키와 관련하여, "Cookie Domain"은 해당 쿠키가 유효한 도메인을 명시한다. 쿠키 도메인이 설정되면, 이 도메인과 서브 도메인에서만 쿠키가 접근 가능하다. 예를 들어, 쿠키 도메인이 .example.com으로 설정되면, www.example.com, subdomain.example.com 등에서 해당 쿠키를 사용할 수 있다. 쿠키 도메인이 명시적으로 설정되지 않으면, 쿠키는 그 쿠키를 생성한 서버의 도메인에 대해서만 접근 가능하다.Origin은 웹 보안에서 매우 중요한 개념으로, 프로토콜, 호스트(도메인과 포트 번호를 포함) 그리고 포트를 포함하여 정의된다.

 

Origin은 콘텐츠의 출처를 식별하고, 다양한 보안 정책(예: SOP, CORS 등)에서 중요한 역할을 한다. 예를 들어, https://www.example.com:443에서 프로토콜은 https, 호스트는 www.example.com, 포트는 443이다. 이 모든 요소가 같아야 동일 출처로 간주된다.Host vs. Domain: Host는 특정 웹 서버를 가리키는 데 사용되며 포트 번호를 포함할 수 있다. 반면, Domain은 더 넓은 범위로, 같은 도메인 내에서 쿠키 등의 공유 자원을 관리하는 데 사용된다.

 

Domain vs. Origin: Domain은 보통 웹 주소의 일부로 쓰이며 쿠키와 같은 웹 리소스의 범위를 정의하는 데 사용된다. Origin은 프로토콜, 호스트, 포트 모두를 명시적으로 포함하여 웹 콘텐츠의 출처를 보다 엄격하게 정의한다. Origin이 같아야만 웹 페이지간 데이터를 자유롭게 공유할 수 있다.Host vs. Origin: Host는 도메인 이름과 선택적으로 포트 번호를 포함하지만, Origin은 프로토콜, 도메인, 포트 모두를 명시적으로 포함한다. 보안 정책 적용 시 Origin이 더 엄격하게 사용된다.

 

SameSite 쿠키는 웹사이트가 사용자의 브라우저와 상호 작용하는 방식을 보다 세밀하게 제어할 수 있게 하는 쿠키의 속성 중 하나이다. 이 속성은 크로스 사이트 요청 보안(Cross-Site Request Forgery, CSRF) 공격을 방지하는 데 효과적으로 사용될 수 있다. SameSite 속성은 쿠키가 한 웹사이트에서 생성되어 다른 웹사이트와의 상호 작용에서 사용되는 것을 제한한다.

 

SameSite=Strict로 설정된 쿠키는 쿠키를 생성한 동일한 사이트에서만 보내진다. 예를 들어, 사용자가 www.example.com에서 생성된 쿠키는 오직 www.example.com에서만 읽을 수 있다.이 설정은 사용자가 링크를 클릭하여 다른 사이트로 이동했을 때, 쿠키가 전송되지 않는다는 것을 의미한다. 이로 인해 사용자 경험이 제한될 수 있지만, 보안은 매우 강화된다.

 

SameSite=Lax 설정은 좀 더 유연하다. 이 설정은 사용자가 외부 사이트에서 현재 사이트로 자연스럽게 넘어갈 때 (예: 링크 클릭을 통해) 쿠키를 전송한다.그러나 외부 사이트에서 발생하는 POST 요청에서는 쿠키를 전송하지 않는다. 이는 대부분의 CSRF 공격 시나리오에서 보호할 수 있다. 이 설정은 사용자 경험을 크게 훼손하지 않으면서도 필수적인 보안을 제공한다.

 

SameSite=None 설정은 쿠키가 모든 출처의 요청에 대해 전송되도록 허용한다. 이 설정을 사용하려면 쿠키는 반드시 Secure 플래그도 설정되어야 하며, 쿠키는 오직 보안 연결(HTTPS)을 통해서만 전송된다.이 옵션은 다양한 도메인 간에 세션을 유지해야 하는 애플리케이션에서 유용하다.

 

SameSite 속성은 웹 애플리케이션을 CSRF 공격과 일부 크로스 사이트 스크립팅(XSS) 공격으로부터 보호하는 데 도움을 준다.개발자는 이 속성을 사용하여 애플리케이션의 쿠키 전송 정책을 더 세밀하게 제어할 수 있다.특히 Lax 설정은 대부분의 사용 사례에 적합한 보안과 사용성의 균형을 제공한다. 브라우저들은 SameSite 속성을 지원하고 있으며, 웹 개발자들은 이 옵션을 적절히 사용하여 애플리케이션의 보안을 강화할 수 있다.

 

웹 개발에서 사용자 데이터를 저장하는 방법으로 Cookies, localStorage, sessionStorage 세 가지가 주로 사용된다. 이 저장 방법들은 각기 다른 특성과 용도를 가지며, 사용 사례에 따라 적절한 저장소를 선택하는 것이 중요하다.

 

Cookies는일반적으로 4KB로 제한된다. 쿠키는 만료 날짜를 가질 수 있으며, 설정된 날짜가 지나면 자동으로 삭제된다. Secure, HttpOnly, SameSite 등의 플래그를 설정하여 보안을 강화할 수 있다. 모든 HTTP 요청에 쿠키 정보가 포함되어 서버로 전송되므로, 네트워크 트래픽에 영향을 줄 수 있다.사용자 인증, 사용자 설정 등을 관리하며, 서버가 사용자를 식별하도록 돕는다.

 

localStorage는 대부분의 브라우저에서 5MB~10MB 사이의 데이터를 저장할 수 있다. 브라우저가 닫혀도 데이터가 지속적으로 저장되며, 수동으로 삭제하지 않는 한 유지된다. 데이터가 서버로 전송되지 않아 네트워크 부하가 없다. 같은 도메인 및 프로토콜 내에서만 접근할 수 있다. 사용자 설정, 사이트 기본 설정, 대량의 데이터 저장 등에 사용된다.

 

sessionStorage는 localStorage와 비슷하게 5MB~10MB 사이다. 브라우저 탭이 열려 있는 동안에만 데이터가 유지되며, 탭을 닫으면 데이터가 삭제된다. 데이터가 서버로 전송되지 않아 네트워크 부하가 없다. localStorage와 같이 같은 도메인 및 프로토콜 내에서만 접근할 수 있다. 각 브라우저 탭에 독립적인 세션 데이터를 유지하는 데 사용되며, 예를 들어 온라인 폼 작성 중 페이지를 새로 고침해도 데이터가 유지되도록 한다.

728x90
728x90

HTTP는 웹에서 데이터를 주고받기 위한 프로토콜로, 클라이언트와 서버 간의 통신을 가능하게 한다. 클라이언트(대개 웹 브라우저)는 서버에 페이지나 데이터를 요청하고, 서버는 그 요청에 응답하여 필요한 정보를 제공한다. 서버는 클라이언트의 이전 상태를 기억하지 않으며, 각 요청은 독립적이다.

 

이러한 특성 때문에 HTTP는 "무상태(stateless)" 프로토콜로 분류된다. HTTP 통신은 일반적으로 신뢰성 있는 TCP/IP 프로토콜을 사용하여 이루어지며, 이 프로토콜은 다양한 데이터 형식을 지원하고 새로운 기능을 쉽게 추가할 수 있는 유연성을 제공한다.

 

HTTP는 기본적으로 암호화되지 않기 때문에 데이터가 네트워크를 통해 전송될 때 외부로부터 쉽게 엿보거나 변조될 수 있다는 단점이 있다. 이러한 보안 문제를 해결하기 위해 HTTPS가 도입되었다. HTTPS는 HTTP의 보안 버전으로, SSL(Secure Sockets Layer) 또는 TLS(Transport Layer Security) 프로토콜을 사용하여 클라이언트와 서버 간의 통신을 암호화한다. 이는 데이터의 기밀성과 무결성을 보장하며, 서버나 클라이언트가 자신이 주장하는 대로 신뢰할 수 있는지 확인하는데 중요한 역할을 한다.

 

데이터 보안과 관련하여, HTTPS는 중간자 공격(Man-In-The-Middle Attack)을 방지하고, 크로스 사이트 스크립팅(XSS), 세션 하이재킹, 크로스 사이트 리퀘스트 포저리(CSRF)와 같은 다양한 웹 기반 공격으로부터 사용자를 보호한다.

 

또한, HTTPS는 데이터를 암호화하여 통신 내용이 외부에 노출되는 것을 방지한다. 이는 데이터가 전송 중에 손상되거나 변경되지 않았음을 보장하고, 웹사이트의 신뢰성을 사용자에게 보장하는 데 도움을 준다. SSL/TLS 인증서는 웹사이트의 신원을 인증받을 수 있으며, 사용자가 실제로 의도한 서버와 통신하고 있음을 확인시켜 준다.

 

Mixed Content 발생은 HTTPS로 보호된 웹 페이지에서 HTTP를 통해 로드된 리소스가 포함될 때 일어난다. 이는 웹 보안을 약화시킬 수 있다.

 

능동적인 Mixed Content는 웹 페이지와 상호 작용하며 보안 위험을 증가시키고, 수동적인 Mixed Content는 페이지의 기밀성을 손상시키지는 않지만 보안 상태에 대한 잘못된 인식을 유발할 수 있다. 현대의 브라우저는 대부분 Mixed Content를 차단하고, 개발자들은 모든 리소스를 HTTPS를 통해 제공하도록 권장받는다.

 

SSH, TLS, GPG, 그리고 HTTP/2는 모두 네트워크와 데이터 전송을 안전하게 보호하는 데 필수적인 기술들이다. SSH는 네트워크 서비스를 안전하게 운영하며 원격 시스템 관리와 파일 전송에 사용된다. TLS는 데이터의 암호화를 통해 네트워크 통신의 보안을 강화하고, GPG는 데이터의 암호화와 디지털 서명을 제공한다.

 

HTTP/2는 웹의 성능을 향상시키기 위해 설계되었으며, 다중화와 헤더 압축을 통해 더 빠른 웹 경험을 제공한다. 이 기술들은 통신의 보안을 강화하며, 사용자 데이터를 보호하는 데 중요한 역할을 한다. 각 기술은 특정 목적에 맞게 설계되었으며, 안전한 파일 전송, 네트워크 통신의 보안 강화, 개인 데이터의 보호 등에 사용된다.

728x90
728x90

크롬 익스텐션은 manifest를 루트로 로드해야한다.

{
    "manifest_version": 3,
    "name": "Learn on Air",
    "version": "1.0",
    "description": "Learn on Air for Chrome && inflearn",
    "permissions": [
      "activeTab",
      "tabs",
      "storage",
      "https://*/*"
    ],
    "action": {
      "default_popup": "./src/popup/popup.html",
      "default_icon": {
        "16": "public/16.png",
        "48": "public/48.png",
        "128": "public/128.png"
      }
    },
    "content_scripts": [
      {
        "matches": ["http://*/*", "https://*/*"],
        "js": ["./content/contentScript.js"]
      }
    ],
    "icons": {
      "16": "public/16.png",
      "48": "public/48.png",
      "128": "public/128.png"
    }
  }

 

contentScript는 ES6모델을 지원하지 않기 때문에, import와 export가 지원이 안되는데, 이때메 컴포넌트별로 나누어 작업하려면 웹팩을 이용해서 import된 파일을 빌드시켜서 해당 dist를 로드시켜야한다.

    const infoWindow = document.createElement('div');
    infoWindow.id = 'infoWindow';
    infoWindow.style.width = '300px';
    infoWindow.style.height = '500px';
    infoWindow.style.position = 'absolute';
    infoWindow.style.top = '140px';
    infoWindow.style.left = '50px';
    infoWindow.style.backgroundColor = 'lightgray';
    infoWindow.style.border = '1px solid gray';
    infoWindow.style.zIndex = '1001';
    infoWindow.style.padding = '10px';
    infoWindow.innerHTML = `
    <div style="position: absolute; top: 1px; right: 3px; cursor: pointer;">
        &#10005;
    </div>
    <p style="margin-top: 13px; margin-bottom: 17px;">연결상태: 양호</p>
    <p style="margin-top: 13px; margin-bottom: 17px;">졸음상태: 0회, (00:00:00)</p>
    <p style="margin-top: 13px; margin-bottom: 17px;">자리이탈: 0회, (00:00:00)</p>
    <p style="margin-top: 13px; margin-bottom: 17px;">녹화여부: 허용</p>
    <p style="margin-top: 13px; margin-bottom: 17px;">캠화면:</p>
    `;

 

contentScript로 내부기능, 비기능을 전부 작성해야하기 때문에, 자바스크립트에서 HTML을 작성해야했다. React와 Typescript로 작성된 보일러플레이트를 사용하면 웹팩 등 설정들이 다 되어있었는데, 그 코드는 생각보다 나와의 개발스타일도 다르고 빌드과정을 이해하기에는 시간이 없을 것 같아서 바닐라로 처음부터 짜기로 하였다.

if (!isActive) {
        const scriptButton = Array.from(document.querySelectorAll('.css-zl1inp')).find(li => {
            const button = li.querySelector('button');
            return (button && button.getAttribute('title') === '스크립트') || (button && button.getAttribute('title') === '성장 로그');
        });

        if (scriptButton) {
            const li = document.createElement('li');
            li.className = 'css-zl1inp';
            li.id = 'learningAssistantIcon';
            li.innerHTML = `
                <button class="mantine-UnstyledButton-root mantine-Button-root mantine-syxma7" type="button">
                    <div class="mantine-1yjkc96 mantine-Button-inner">
                        <span class="mantine-1vgkxjh mantine-Button-label">
                            <img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw6E6e%2FbtsGRJCy10B%2FDorSShP9f3AJjKRGQnCAZk%2Fimg.png">
                            <p class="mantine-Text-root mantine-qjo01i">학습 보조</p>
                        </span>
                    </div>
                </button>
            `;

 

인프런의 무료강의와 유료강의의 사이드바의 아이콘 수가 다르기 때문에 OR로 지정해주고 해당 아이콘 밑에 학습 보조라는 새로운 아이콘을 삽입시켜주었다.

이제 사용자의 졸음, 자리이탈, 영상 중간에 퀴즈를 정중앙에 나오게 하기 등의 기능을 익스텐션에서 개발하고 팀원들이 따로 개발한 웹페이지에선 사용자들이 퀴즈를 만들 수 있는 기능을 제공할 예정이다. 영상처리모델은 Mediapipe가 정확도나 속도면에서 매우 뛰어났기 때문에 Mediapipe의 landmarker을 채택할 것 같다.

 

728x90
728x90

웹 워커(Web Workers)는 자바스크립트에서 멀티스레딩을 구현할 수 있는 기능이다. 자바스크립트는 기본적으로 싱글 스레드로 동작하기 때문에, 복잡하고 시간이 많이 걸리는 작업을 수행할 때 UI가 멈추지 않도록 별도의 백그라운드 스레드에서 코드를 실행할 필요가 있다.

 

웹 워커를 사용하면 메인 스레드가 아닌 별도의 스레드에서 스크립트를 실행할 수 있어, 메인 스레드(주로 UI를 담당)는 사용자 인터랙션과 화면 업데이트에 집중할 수 있고, 워커 스레드에서는 계산이 많이 필요한 작업을 동시에 처리할 수 있다. 이로 인해 애플리케이션이 더욱 빠르고 반응적으로 동작할 수 있다.

 

웹 워커는 데이터를 메인 스레드와 메시지 형태로 교환하며, 이 메시지는 복사되어 전달되므로 메인 스레드와 워커 스레드 사이에 데이터를 공유하지 않는다. 워커 내에서는 DOM에 직접 접근할 수 없으며, 메인 스레드에서 전달받은 데이터를 기반으로 작업을 수행하고 결과를 메인 스레드로 다시 보낸다.

 

예를 들어, 이미지 처리나 파일 다운로드와 같은 무거운 작업을 워커에서 처리하면 메인 스레드는 이러한 처리가 완료될 때까지 멈추지 않고 계속하여 사용자와의 상호작용을 유지할 수 있다.

 

자바스크립트는 싱글 스레드 언어로, 기본적으로 한 번에 하나의 작업만 수행할 수 있다. 그러나 비동기 프로그래밍 모델을 사용하여 동시성(concurrency)을 구현하고, 여러 작업을 효율적으로 처리할 수 있다. 이를 위한 주요 메커니즘으로 이벤트 루프, 콜백 함수, 프로미스(Promises), async/await 등이 있다.

 

자바스크립트의 실행 환경인 브라우저나 Node.js는 이벤트 루프를 사용하여 연속적인 작업을 처리한다. 이벤트 루프는 콜 스택(Call Stack), 이벤트 큐(Event Queue), 백그라운드 작업을 관리한다.

 

콜 스택이 비어있을 때, 이벤트 루프는 이벤트 큐에서 콜백 함수를 하나씩 콜 스택으로 옮겨 실행한다. 이러한 구조 덕분에 자바스크립트는 비동기 콜백을 활용하여 여러 작업을 동시에 처리할 수 있다.

 

프로미스는 비동기 작업의 최종 성공 또는 실패를 나타내는 객체이다. 콜백 지옥을 피하고, 비동기 코드의 가독성과 작성 편의성을 높이는 데 도움을 준다. 프로미스는 세 가지 상태를 가진다. (대기(pending), 이행(fulfilled), 거부(rejected))

const promise = new Promise((resolve, reject) => {
  if (/* 조건 */) {
    resolve(value); // 작업 성공
  } else {
    reject(error); // 작업 실패
  }
});

promise.then(result => {
  console.log(result); // 성공 결과 처리
}).catch(error => {
  console.error(error); // 에러 처리
});

 

 

async/await는 프로미스 기반의 비동기 코드를 동기 코드처럼 쉽게 작성할 수 있게 해주는 ES2017(ES8)의 문법이다. async로 함수를 정의하면 그 함수는 자동으로 프로미스를 반환한다. await 키워드는 프로미스가 처리될 때까지 함수 실행을 일시 중지하고, 결과 값 또는 에러가 반환될 때까지 기다린다.

async function fetchData() {
  try {
    const data = await fetch('url'); // 프로미스가 이행될 때까지 기다림
    const json = await data.json();
    console.log(json);
  } catch (error) {
    console.error(error);
  }
}

 

이러한 메커니즘을 통해 자바스크립트는 싱글 스레드임에도 불구하고 다수의 비동기 작업을 효과적으로 관리하고, 동시에 여러 작업을 수행할 수 있는 동시성을 제공한다.

728x90

+ Recent posts