npm의 의존성 관리

조금 더 자세한 설명은 아래와 같다. npm은 아래 5가지 종류의 의존성을 제공하고 있다.

  • dependencies
    • 프로젝트에서 실제로 의존하고 호출하는 의존성들
  • devDependencies
    • 개발할 때만 의존하는 의존성들(예를 들면 코드 포맷팅을 예쁘게 해주는 라이브러리)
  • peerDependencies
    • 내 패키지가 다른 패키지로부터 직접 불려지는(require) 것은 아니지만 특정 버전의 패키지와 호환된다는 것을 명시
    • 즉, 내가 다른 패키지의 특정 버전과 호환된다는 것을 뜻한다
    • 자세한 내용은 여기 참고
  • optionalDependencies
    • 선택적인 의존성으로 없거나 설치에 실패해도 npm 패키지 설치 과정이 중단되지 않아 다른 라이브러리 설치에 영향을 주지 않는 의존성들
  • bundledDependencies
    • 내 패키지와 함께 제공되는 일련의 패키지들. 타사 라이브러리가 NPM에 없거나 일부 프로젝트를 모듈에 포함하려는 경우 사용할 수 있다.

해결 방법

  1. npm install --force

로컬에 다운로드 복제본이 존재하더라도 다시 온라인에서 다운로드 받는다.

  1. npm install --legacy-peer-deps

마치 6버전 이하에서 동작하던 것처럼 peerDependencies를 무시한다.

  1. npm config set legacy-peer-deps true

모든 npm install에서 legacy-peer-deps로 동작하도록 설정한다. npm에서 권장하지 않는 방법이다.

  1. yarn install

 

대용량 데이터 행 삭제 작업 시 실행 조건 없이 삭제 할 경우 성능 감소 및 큰 문제를 몇가지 가지고 있다.

 

SQL 서버는 트랜잭션을 처리하기 위하여 트랜잭션 로그에 모든 변경 사항을 작성하고, 완전한 테이블 잠금을 수행할 것이다.

 

예를 들어 서비스중인 응용프로그램에서 해당 테이블에 액세스를 필요로 하는 경우, 응용프로그램은 테이블을 사용할 수 있을때까지 기다려야 하며. 응용프로그램에 타임아웃이 설정되어있는경우 응용프로그램을 사용하는 사용자는 정상적인 응답결과를 받지 못 할 것이다.

 

해결 방안으로 아래 테스트 코드를 참고하여 TOP LOOP 내에서 행을 반복적으로 삭제 처리해본다.

 

1. Log 테스트용 테이블 생성

CREATE TABLE [dbo].[Log](
    [id] [int] NOT NULL,
    [logDateTime] [datetime] NULL,
 CONSTRAINT [PK_LogTest] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
 

2. While문을 이용한 테스트 데이터 생성
데이터 만건을 넣은후에 테스트.

declare @loopCnt int 
declare @logDateTime datetime

set @loopCnt = 1
set @logDateTime = '2001-01-01'

while (@loopCnt < 10000)
begin

    INSERT INTO [dbo].[Log] ([id],[logDateTime]) VALUES (@loopCnt,@logDateTime)
    set @loopCnt = @loopCnt + 1
    set @logDateTime = DATEADD(second,1,@logDateTime)

end

 

3. DELTE TOP문을 이용한삭제
일괄로 삭제시 시간이 오래 걸리므로 TOP문을 사용해서 데이터를 삭제.

이때 Loop문을 사용하여 DELETE문사이에 적절한 Delay를 주어 시스템 멈추는 부분을 최소화 하길 추천한다. 

삭제문의 경우는 복구가 어려우므로 충분한 테스트를 하신후에 사용하길. 

@@rowcount는 전역변수이므로 delete top문을 loop문 마지막에 위치.

delete top(1000)    from LogTest where logdatetime < '2021-12-01 10:00'

while (@@rowcount > 0)
BEGIN

    --delay 100m/s
    waitfor delay '0:0:0:100'

    delete top (1000)  from Log where logdatetime < '2021-12-01 10:00'

END
 

 

자바스크립트 배열의 내장 함수에 sort()가 있다. 명칭 그대로 배열 안의 원소를 정렬하는 함수이다. 추후에 또 검색할 것 같아서 적어놓는다.

arrayobj.sort(sortFunction)

 
arrayobj는 임의의 Array 개체이다. sortFunction는 요소 순서를 결정하는 데 사용되는 함수의 이름이다.

생략하면 오름차순, ASCII 문자 순서로 정렬된다.

sortFunction 인수에 함수를 지정하면 아래의 값 중 하나가 반환된다.

 

  • 첫 번째 인수가 두 번째 인수보다 작을 경우 - 값
  • 두 인수가 같을 경우 0
  • 첫 번째 인수가 두 번째 인수보다 클 경우 + 값
  • 정렬할 Array의 요소의 개수가 2개 미만일 경우 ‘sort is not a function’ 오류가 난다.

문자 정렬

var fruit = ['orange', 'apple', 'banana'];

/* 일반적인 방법 */
fruit.sort(); // apple, banana, orange

숫자 정렬

var score = [4, 11, 2, 10, 3, 1]; 

/* 오류 */
score.sort(); // 1, 10, 11, 2, 3, 4 
              // ASCII 문자 순서로 정렬되어 숫자의 크기대로 나오지 않음

/* 정상 동작 */
score.sort(function(a, b) { // 오름차순
    return a - b;
    // 1, 2, 3, 4, 10, 11
});

score.sort(function(a, b) { // 내림차순
    return b - a;
    // 11, 10, 4, 3, 2, 1
});

Object 정렬

var student = [
    { name : "재석", age : 21},
    { name : "광희", age : 25},
    { name : "형돈", age : 13},
    { name : "명수", age : 44}
]

/* 이름순으로 정렬 */
student.sort(function(a, b) { // 오름차순
    return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
    // 광희, 명수, 재석, 형돈
});

student.sort(function(a, b) { // 내림차순
    return a.name > b.name ? -1 : a.name < b.name ? 1 : 0;
    // 형돈, 재석, 명수, 광희
});

/* 나이순으로 정렬 */
var sortingField = "age";

student.sort(function(a, b) { // 오름차순
    return a[sortingField] - b[sortingField];
    // 13, 21, 25, 44
});

student.sort(function(a, b) { // 내림차순
    return b[sortingField] - a[sortingField];
    // 44, 25, 21, 13
});

정리

우선 [20, 10, 9,8,7,6,5,4,3,2,1]의  배열이 있을경우 배열에서 a-b라는 연산을 모두 한 다음
그 결과값으로 정렬하는 것이 아니라

 

( a, b ) 형식으로 지정한 두 인자를 차례로 비교한다.

배열 a[0]과 b[1] 즉, 20과 10을 비교 20-10 = 10
결과값이 10 즉, 양수


sort함수에 sortNumber(a,b)의 return 값으로 양수 10을 전달합니다.
그럼 sort함수가 양수값을 전달받고 배열의 순서를 바꾸어 버립니다.
(정확하게 말하면 두 배열 안에 든 값을 교체)


그럼 배열이 [10, 20, 9,8,7,6,5,4,3,2,1] 이렇게 바뀝니다.

그 다음 a[0]과 b[2] 즉 10과 9를 비교합니다. 10 - 9 = 1 >0, 양수입니다.
결과값이 양수이므로 또 10과 9의 순서를 바꿉니다.


이런 식으로 계속 두 인자를 비교해서 결과값이 양수가 나오면 순서를 바꾸고,
음수가 나오면 순서를 그대로 유지.

 

이해하기 쉽게 배열이 바뀌어가는 순서를 보면

[(20), (10), 9,8,7,6,5,4,3,2,1] 20-10 = 10, 즉 양수이므로 순서바뀜! ()는 비교되는 인자값.
[(10), 20, (9),8,7,6,5,4,3,2,1] 10 - 9 = 1 또 양수, 순서 바뀜.
[(9), 20, 10, (8),7,6,5,4,3,2,1] 반복...
[(8), 20, 10, 9,(7)...]
...
[(2). 20, 10...3, (1)]
[(1), 20, 10...]

그럼 배열 내에서 가장 작은 값 1이 찾게되고

[1, 20, 10, 9,8,7,6,5,4,3,2]

1의 순서는 바뀌지 않습니다. 1-2 = -1
즉 결과값이 음수이기 때문.

 

그 다음은 두번째...
20 - 10 = 10 > 0 이므로 순서를 또 바꿉니다.

[1, (20), (10), 9,8,7,6,5,4,3,2]
[1, (10), 20, (9), 8...]
[1, (9), 20, 10, (8)...]

이런 식으로 반복하다 보면 두번째로 작은 값 2도 찾게 됨

....
[1, 2, 20, 10, 9,8,7,6,5,4,3]

 

그럼 다음은 세번째...

이렇게 순차적으로 반복하면 결국 정렬이 진행이 된다고 생각하면 된다.

네트워크를 통해 리소스를 가져오는 것은 무겁고 비쌉니다.

  • 큰 응답은 브라우저와 서버 간 많은 왕복이 필요합니다.
  • 페이지는 모든 필수 리소스가 완전히 다운로드 되기 전에 로드되지 않습니다.
  • 제한된 데이터 플랜을 가진 모바일 폰으로 웹사이트 접근 시, 불필요한 네트워크 요청은 돈낭비가 될 것입니다.

Browser compatibility

모든 브라우저에서 아래 API를 모두 지원합니다.

  • Cache-Control
  • ETag
  • Last-Modified

이전의 가져온 리소스들을 재사용하면 웹 사이트와 애플리케이션의 성능을 향상시킬 수 있습니다. HTTP 캐싱은 웹 사이트가 좀 더 빠르게 반응하도록 만들 수 있습니다.

캐싱: 주어진 리소스의 복사본을 저장하고 있다가 요청시에 제공하는 기술

⇒ 서버의 부하 완화, 성능 향상

⇒ but 리소스가 변할 수 있기 때문에 변하기 전에만 캐싱하고 변한 이후에 더 이상 캐싱하지 않는 것이 중요

캐싱 동작의 대상

  • 검색 요청의 성공적인 결과: HTML 문서, 이미지 혹은 파일과 같은 리소스를 포함하는 GET 요청에 대한 200(OK) 응답
  • 영구적인 리다이렉트: 301(Moved Permanently) 응답
  • 오류 응답: 404 (Not Found)
  • 완전하지 않은 결과: 206(Partial Content)
  • 캐시 키로 사용하기에 적절한 무언가가 정의된 경우의 GET 이외의 응답

캐싱 제어

Cache-Control 헤더

Cache-Control 헤더 필드는 요청과 응답 양쪽에 있어 캐싱 메커니즘을 위한 디렉티브를 지정하는데 사용합니다.

// 클라이언트 요청, 서버응답 모두 저장 안함, 요청은 매번 서버로 전송되고 전체 응답은 매번 다운로드
Cache-Control: no-store
// 캐시된 복사본을 사용하지만 먼저 서버에 요청해 유효성 검사하도록 강제
Cache-Control: no-cache
// 리소스가 공개적으로 캐싱될 수 있음
Cache-Control: public
// 단일 사용자만을 위한 것이고 공유 캐시에 의해 저장되서는 안된다.
Cache-Control: private
// 유효하다고 판단되는 최대 시간
Cache-Control: max-age=31536000
// 상태를 확인하고 만료된 리소스는 사용 안함, HTTP는 특정상황에서 fresh하지 않은 데이터도 사용하는 경우가 있는데 이를 막기위해 사용
Cache-Control: must-revalidate

유효성

어떤 리소스들은 서버 상에서 변경될 수 있고 캐시가 갱신되어야 합니다. 서버는 리소스에 대한 max-age를 리턴합니다. max-age 이전의 리소스는 fresh하고, 이후의 리소스는 stale하다고 합니다. stale된 리소스에 대한 요청을 받은 경우, 이 리소스가 현재 유효한지 여부를 판단하기 위해 If-None-Match(en-US)와 함께 요청을 전달하고 서버는 304 (Not Modified) 헤더를 돌려보내 대역폭을 절약합니다. max-age는 Cache-control: max-age=N 헤더 ⇒ Expires 헤더 ⇒ Last-Modifed 헤더 순으로 결정됩니다.

ETags

Etag Response 헤더는 사용자가 알 수 없는 값으로, 리소스에 대한 응답에 포함된다면 클라이언트는 이후 Request의 헤더 안에 If-None-Match(en-US)를 줄 수 있습니다.

Last-Modified

Last-Modified Response 헤더는 약한 검증을 위해 사용됩니다. Response 내에 Last-Modified 헤더가 존재하면 클라이언트는 캐시된 문서를 검증하기 위해 if-Modified-Since Requset 헤더를 줄 수 있습니다.

versioned URL들을 위한 장기간 캐싱

fingerprint나 버전 정보가 포함된 URL을 위한 요청에 응답할 때, 그리고 컨텐츠가 변경되지 않을 거라고 예상된다면 응답에 Cache-Control: max-age=31536000 를 추가할 수 있습니다. 31,536,000초(1년)는 지원하는 최대 값입니다. 웹팩에서 아래 설정을 통해 asset의 URL에 hash fingerprint를 할당할 수 있습니다.

output: {
   // filename: 'bundle.js',
   filename: '[name].[contenthash].js',
   path: path.resolve(__dirname, 'dist'),
   clean: true,
},

파일 내용이 변경되지 않더라도 [hash]값이 변경될 수 있는데, 웹팩에서 빌드 시에 항상 초기화 코드를 포함하기 때문입니다. 웹팩에서는 runtime 코드를 분리하기 위해 optimization.runtimeChunk option을 사용할 수 있는데요. single로 설정하면 모든 chunk들을 위한 하나의 runtime 번들을 생성합니다.

optimization: {
   runtimeChunk: 'single',
 },

자세한 내용은 https://webpack.js.org/guides/caching/#output-filenames 에서 확인할 수 있습니다.

unversioned URL들을 위한 서버 재검증

모든 URL이 버전 정보를 포함할 수 없습니다. Cache-Control 값을 통해 캐시 정책을 튜닝 할 수 있습니다.

Summary

  • 캐시를 사용하면 서버의 부하를 줄이고, 성능을 높힐 수 있다.
  • GET 요청의 200 응답, 301 응답, 404 응답, 206응답을 대상으로 동작한다.
  • 버전 정보가 포함된 URL은 재검증이 필요없으므로 신뢰성과 속도를 모두 얻을 수 있다. Webpack에서 output파일에 output filename에 hash를 사용하여 최적화 할 수 있다.

Reference

https://web.dev/http-cache/#flowchart

https://developer.mozilla.org/ko/docs/Web/HTTP/Caching

'기타 > 네트워크' 카테고리의 다른 글

L4 switch L7 switch  (0) 2021.08.23
최신 메시지 큐(Messgae Queue) MQ 기술  (0) 2019.09.02

+ Recent posts