[NODE,NEST] SERVICE_KEY_IS_NOT_REGISTERED_ERROR 해결

 

요즘 node랑 nest 공부 하면서 간단히 이것저것 만들어 보고 있다가.

공공데이터 포탈에서 어떤 API를 가져오려고 작업을 했었다.

그런데 간헐적으로 SERVICE_KEY_IS_NOT_REGISTERED_ERROR  에러가 자꾸 발생을 하는 

현상을 발견했다.

<cmmMsgHeader>
    <errMsg>SERVICE ERROR</errMsg>
    <returnAuthMsg>SERVICE_KEY_IS_NOT_REGISTERED_ERROR</returnAuthMsg>
    <returnReasonCode>30</returnReasonCode>
</cmmMsgHeader>

 

이런 오류가 자꾸 떨어지는데 

분명 포스트맨에서 테스트 할때는 쭉 정상으로 작동을 했는데 

node서버에서 돌릴땐 어떨땐 정상으로 받아오고 어떨땐 저 에러가 발생을 하는 현상이 있어

이것저것 해봤는데 결국은 인코딩 문제였다. 

왜 간헐적으로 정상응답과 오류응답이 나타나는건지 몰라 한참을 해매다가

그냥 차라리 인코딩을 하지 말고 보내 보자 하고 해보니 저 에러가 나타나지 않고 정상작동 하는것을 확인 했다.

 

// firstValueFrom 함수를 사용하여 Observable을 Promise로 변환
      const response = await firstValueFrom(
        this.httpService.get(
          {
            params: queryParams,
            responseType: 'text', // XML 응답을 처리하기 위해 responseType을 'text'로 설정
          },
        ),
      );

이런식으로 요청을 보내고 있었는데 

확인해보니 

 

HttpService (Axios 기반)를 사용할 때, URL 파라미터는 자동으로 인코딩이 된다고 한다.

Axios는 내부적으로 encodeURIComponent 함수를 사용하여 URL 파라미터의 항목들을 인코딩하기 때문에,

별도로 인코딩 처리를 하지 않아도 된다고 한다.

기존에는 당연히 인코딩해서 보내줘야지 하고 parmas에 보내는 값중 키값을

const encodedApiKey = encodeURIComponent(serviceKey); // API 키 인코딩

이렇게 사용하고 있었는데 

자동으로 인코딩이 한번 더 되버리니 키값에 문제가 생긴것인다.

저 인코딩 해주는 부분을 빼고 디코드 키를 바로 사용하면 해결이 된다.

그럼 그냥 전부 다 오류응답으로 나와야 하는데 왜 되다 안되다 하는것인지는 모르겠다;;;

 

 

Cannot find module 'msw/node' 에러 해결

 

NEXT.js 14버전 환경에서 jest를 사용한 테스트 환경 구축중에 

api요청등의 문제를 모킹서버로 해결하기 위해 

msw설치후 테스트를 돌렸는데 

 

msw/node 를 찾지 못한다는 에러가 발생을 하였다.

분명 패키지제이슨엔 설치가 되어 있는데도 오류가 발생하고 있어 

chatGpt에 물어봐도 다시 설치하라는 말만 하고 있어 다른 방법을 찾아 보던중

 

https://github.com/mswjs/msw/issues/1786

 

"Cannot find module 'msw/node'" in Jest JSDOM environment · Issue #1786 · mswjs/msw

Prerequisites I confirm my issue is not in the opened issues I confirm the Frequently Asked Questions didn't contain the answer to my issue Environment check I'm using the latest msw version I'm us...

github.com

이곳의 글을 확인 하였고 코멘트에서 해결방법을 찾았다.

 

jest.config.ts 파일의 설정중에 

testEnvironmentOptions: {
    customExportConditions: [""],
  },

이부분을 추가 하면 된다. 

에러가 난 이유를 설명한걸 해석해 보자면

 

JSDOM이 내보내기 조건을 강제하기 때문입니다 browser.

즉, JSDOM은 "타사 패키지가 browser필드를 내보내는 경우 해당 필드를 사용하십시오"라고 말합니다.

그것이 기본값이고 다소 위험한 기본값입니다. 왜? JSDOM은 여전히 ​​Node.js에서 실행되기 때문입니다 .

게다가 JSDOM은 설계상 100% 브라우저 호환성을 가질 수 없으므로 browser내보내기 조건을 강제하면

MSW와 같이 다양한 환경에 대해 다양한 코드를 제공하는 패키지로 작업할 때 테스트가 필요 이상으로 실패하게 됩니다.

이렇게(위와 같은 설정을) 하면 JSDOM이 올바른 동작인 node(또는 ) 내보내기 조건을 사용하게 됩니다 .default

이번 변경 이후 다른 수입 관련 문제가 발생하는 경우 관련이 없으므로 별도로 처리해야 합니다.

모든 사람이 따를 수 있도록 이 권장 사항을 마이그레이션 가이드에도 추가합니다.

 

라고 한다. 

org.springframework.web.reactive.function.client.WebClientRequestException: readAddress(..) failed: Connection reset by peer; 오류 해결

 

현재 프론트엔드로 개발하고 있는데 간헐적으로 서버를 통해 외부 API를 요청할때 오류가 발생하는것을 확인 했다.

여기저기 찾아보니 

WebClinet를 사용해서 외부에 요청을 보낼때 대상서버의 연결이 닫혔을때 나타나는 오류 라고 한다. 

일단 의심이 가는 시나리오는

우리쪽 A서버에서 외부 B서버로 API 요청을 보내고 커넥션을 맺을때 B서버가 중간에 재실행 혹은

별도의 문제로 서버다운이 발생하는 경우라고 생각하여 해당 업체에 문의해본 결과

서버가 내려간적은 없다고 한다. 

 

두번째 시나리오는 B서버가 로드밸런서를 사용해 A서버에서 최초 요청시 B-1에 커넥션을 맺고 통신을 하다

다시 요청시에는 B-2 서버로 요청을 하는 경우

세번째는 로드밸런서 자체의 타임아웃이 A서버보다 빠른 경우

 

확인을 위해 해당 업체에 문의한결과 로드밸런서를 사용중이며 타임아웃이 10분이라고 한다.

 

오류 재현을 위해 테스트해보니 최초 요청 이후 10분후 재요청을 했을 경우에만 동일한 오류가 나는것을 확인하고

세번째 문제가 이유라고 판단 하였고 

다른분들이 작성한 깃이나 블로그의 도움을 받아 수정 할수 있도록 백엔드 팀에 전달하였다.

문제 해결을 위해 참고한 블로그및 사이트들이다.

참고 사이트

https://velog.io/@youngerjesus/Connection-Reset-by-Peer-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0

 

Connection Reset by Peer 문제 해결

Client 가 요청을 보냈는데 서버쪽에서 연결이 닫혔다고 다시 연결하라는 RST (Reset) 패킷을 보내는 경우에 이 에러가 발생한다.Connection prematurely closed BEFORE response 이렇게 쓰기도 한다. Client-Server 연

velog.io

https://jskim1991.medium.com/spring-boot-how-to-solve-webclient-connection-reset-by-peer-error-b1fa38e4106a

 

[Spring Boot] How to solve WebClient Connection reset by peer error

I had a requirement to fetch user data from an external system. It was implemented using WebClient as part of declarative http client…

jskim1991.medium.com

https://github.com/reactor/reactor-netty/issues/1774

 

Connection reset by peer exception · Issue #1774 · reactor/reactor-netty

We have a micro service based spring boot architecture where we are using spring webclient (which internally uses reactor netty) for internal communication between services. The issue that we faced...

github.com

 

 

해결방법

일단 해결 방법은 B서버 로드밸런서의 타임아웃시간에 도달하기 전에 우리쪽에서 먼저 연결을 해제하고

다시 커넥트를 거는것으로 해결 하였고 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Configuration
public class HttpProxyConfiguration {
 
    @Value("${tracker.url}")
    private String trackerUrl;
 
    @Bean
    TrackerClient trackerClient(WebClient.Builder builder) {
        ConnectionProvider provider = ConnectionProvider.builder("fixed")
                .maxConnections(500)
                .maxIdleTime(Duration.ofSeconds(20))
                .maxLifeTime(Duration.ofSeconds(60))
                .pendingAcquireTimeout(Duration.ofSeconds(60))
                .evictInBackground(Duration.ofSeconds(120)).build();
 
        HttpClient httpClient = HttpClient.create(provider);
        httpClient.warmup().block();
 
        var reactorClientHttpConnector = new ReactorClientHttpConnector(httpClient);
 
        var wc = builder.baseUrl(trackerUrl)
                .clientConnector(reactorClientHttpConnector)
                .build();
 
        var wca = WebClientAdapter.forClient(wc);
        return HttpServiceProxyFactory.builder()
                .clientAdapter(wca)
                .build()
                .createClient(TrackerClient.class);
    }
}
cs

 

이 코드를 참조하여 문제를 수정한것을 확인 할 수 있었고

위 코드의 각 부분의 설명을 보자면

  1. @Value("${tracker.url}"): 이 어노테이션은 tracker.url이라는 이름의 프로퍼티 값을 trackerUrl 변수에 주입(inject)합니다. 이 프로퍼티 값은 외부 트래커 서비스의 기본 URL을 포함하며, 애플리케이션 구성 파일(예: application.properties 또는 application.yml)에서 정의됩니다.
  2. TrackerClient trackerClient(WebClient.Builder builder) 메서드: 이 메서드는 TrackerClient 인터페이스의 구현체를 생성하고 구성합니다. 이 인터페이스는 외부 트래커 서비스와의 통신을 위한 메서드를 정의합니다.
  3. ConnectionProvider 설정: ConnectionProvider는 WebClient의 네트워크 연결을 관리하는 데 사용됩니다. 여기서는 최대 연결 수, 최대 유휴 시간, 연결의 최대 수명, 대기 중인 연결 획득 타임아웃 등을 설정합니다. 이 설정은 서비스와의 통신 중 발생할 수 있는 다양한 시나리오를 관리하기 위한 것입니다.
  4. HttpClient 생성 및 설정: HttpClient.create(provider)를 사용하여 ConnectionProvider를 사용하는 HttpClient 인스턴스를 생성합니다. httpClient.warmup().block() 호출은 HttpClient를 "온기"시키며, 네트워크 연결을 미리 설정하여 첫 번째 요청의 지연 시간을 줄이는 데 도움이 됩니다.
  5. WebClient 구성 및 생성: WebClient.Builder 인스턴스에 기본 URL, 클라이언트 커넥터 등을 설정하여 WebClient 인스턴스를 생성합니다. 이 WebClient 인스턴스는 TrackerClient의 HTTP 요청을 실행하는 데 사용됩니다.
  6. HttpServiceProxyFactory를 사용한 TrackerClient 생성: 마지막으로, HttpServiceProxyFactory와 WebClientAdapter를 사용하여 WebClient를 기반으로 하는 TrackerClient 인스턴스를 생성합니다. 이를 통해 외부 트래커 서비스와의 통신을 위한 프록시 클라이언트를 얻게 됩니다.

 

이렇게 정리될수 있으며 ConnectionProvider의 옵션은

  • builder("fixed"): 연결 풀의 이름을 "fixed"로 설정합니다. 이 이름은 로깅이나 디버깅 시 해당 연결 풀을 식별하는 데 사용될 수 있습니다.
  • maxConnections(500): 연결 풀이 동시에 유지할 수 있는 최대 연결 수를 500개로 설정합니다. 이는 애플리케이션이 동시에 열 수 있는 최대 연결 수를 의미하며, 이 한도를 초과하는 연결 요청은 대기 상태가 될 수 있습니다.
  • maxIdleTime(Duration.ofSeconds(20)): 연결이 유휴 상태(즉, 데이터를 전송하지 않는 상태)로 있을 수 있는 최대 시간을 20초로 설정합니다. 유휴 시간이 이 값을 초과하면 연결이 자동으로 종료됩니다.
  • maxLifeTime(Duration.ofSeconds(60)): 연결이 생성된 후 유지될 수 있는 최대 시간을 60초로 설정합니다. 이 시간이 지나면, 연결은 사용 여부에 관계없이 종료됩니다.
  • pendingAcquireTimeout(Duration.ofSeconds(60)): 연결을 획득하기 위해 대기하는 최대 시간을 60초로 설정합니다. 연결 풀에서 사용 가능한 연결을 얻기 위해 이 시간을 초과하여 대기하는 요청은 실패하게 됩니다.
  • evictInBackground(Duration.ofSeconds(120)): 비활성 연결을 정리하는 배경 작업의 실행 간격을 120초로 설정합니다. 이 설정은 연결 풀에서 오래되거나 더 이상 필요하지 않은 연결을 주기적으로 제거하는 데 사용됩니다.

위와 같다.

기존코드는 위의 옵션에 대한 별도의 설정이 없었기에 해당 오류가 발생하였었다.

write /var/lib/docker/tmp/GetImageBlob3644316352: no space left on device 오류 해결

 

현상

요청사항 수정후 개발서버 브런치에 머지까지 끝냈는데 

해당 화면에 수정된 내용이 반영이 되지 않는 현상이 있었다.

젠킨스에 접속해 로그를 확인해봤다.

동일 스텝에서 성공적으로 돌아갔을때와 실패했을때를 비교해보니 

실패한 경우의 로그에서 no space left on device 라는 로그가 있었고 

확인해보니 디스크 공간 부족이 이유 였다. 

이 메시지는 젠킨스 빌드 도중에 Docker 이미지를 작성하거나 가져오려고 할 때 발생하는 오류라고 한다.

이 오류의 핵심은 "no space left on device"로, 이는 Docker가 실행 중인 서버의 디스크 공간이 부족하여

더 이상 데이터를 저장할 수 없을때 나타나는 이유라고 한다. 

해결방법

1. 디스크 공간 확인:

먼저 서버에서 사용 가능한 디스크 공간을 확인하세요. df -h 명령어를 사용하면 현재 디스크 사용량과 사용 가능한 공간을 볼 수 있습니다.

2. 불필요한 Docker 이미지 및 컨테이너 정리:

Docker 이미지와 컨테이너는 시간이 지남에 따라 상당한 양의 디스크 공간을 차지할 수 있습니다. 불필요한 이미지, 컨테이너, 볼륨, 네트워크를 정리하여 공간을 확보할 수 있습니다. 다음 명령어를 사용할 수 있습니다:
불필요한 컨테이너 정리: docker container prune
사용하지 않는 이미지 정리: docker image prune -a
사용하지 않는 볼륨 정리: docker volume prune
사용하지 않는 네트워크 정리: docker network prune
위의 모든 것을 한 번에 정리: docker system prune -a


3.디스크 확장: 

서버의 디스크 공간이 지속적으로 부족한 경우, 디스크를 확장하는 것을 고려해야 할 수 있습니다. 이는 서버의 구성과 사용 중인 클라우드 서비스 제공업체에 따라 달라질 수 있습니다.
로그 파일과 임시 파일 확인: 디스크 공간을 많이 사용하는 다른 원인으로는 크기가 큰 로그 파일이나 임시 파일이 있을 수 있습니다. 이러한 파일들을 정기적으로 확인하고 필요하지 않은 경우 삭제하여 공간을 확보하세요.
저장 정책 검토: Docker 및 Jenkins와 같은 도구들의 저장 정책을 검토하고 조정하여, 필요 이상으로 많은 데이터가 저장되지 않도록 할 수 있습니다.

 

 

라고 GPT가 알려주고 있다. 

 

 

$: 이게 무엇인가?

Svelte에서 $:는 반응성 선언(reactive declarations)을 나타냅니다.

반응성 선언을 사용하면 변수나 표현식이 변경될 때마다 자동으로 업데이트되는 코드를 작성할 수 있습니다.

예를들어

$: ({ postArr } = data); 는 data 객체의 변경을 감지하여 자동으로

postArr 변수를 업데이트하도록 지시하는 코드입니다.

이 구문의 각 부분의 의미는 다음과 같습니다.

$: - 반응성 선언을 시작합니다.
({ postArr } = data) - 객체 구조 분해 할당(object destructuring assignment)를 사용하여 

data 객체에서 postArr 속성을 가져옵니다.


결과적으로, data 객체가 변경될 때마다 postArr 변수가 자동으로 업데이트되어 해당 변경사항을 반영합니다. 

이를 통해 Svelte 컴포넌트에서 데이터가 변경되는 경우 자동으로 UI를 업데이트할 수 있습니다.

 

조금더 자세한 내용은

https://beomy.github.io/tech/svelte/reactivity-syntax/

 

[Svelte] 반응형을 위한 문법

사용자 행동에 따라 반응형으로 동작하는 웹을 만들기 위한 Svelte 문법을 이야기합니다.

beomy.github.io

 

이 페이지를 참고하시면 좋을 것 같습니다.

 

[개인 정보 정책]

[The Pirates]은 앱을 [오픈 소스 / 무료] 앱으로 구축했습니다. 이 서비스는 [The Pirates]에서 [무료] 제공하며 그대로 사용하도록되어 있습니다.

이 페이지는 [The Pirates] 서비스를 이용하기로 결정한 경우 개인 정보의 수집, 사용, 공개와 관련하여 [The Pirates] 정책에 대해 방문자에게 알리는 데 사용됩니다. [The Pirates] 서비스 이용을 선택하시면 본 정책과 관련된 정보 수집 및 이용에 동의하는 것입니다. [The Pirates] 가 수집하는 개인 정보는 서비스 제공 및 개선을 위해 사용됩니다. [The Pirates] 는 이 개인 정보 보호 정책에 설명 된 경우를 제외하고는 누구와도 귀하의 정보를 사용하거나 공유하지 않습니다. 본 개인 정보 보호 정책에서 사용 되는 용어는 본 개인 정보 보호 정책에 달리 정의되지 않는 한 [The Pirates]에서 액세스 할 수있는 이용 약관과 동일한 의미를 갖습니다.

[정보 수집 및 사용]

더 나은 경험을 위해 서비스를 사용하는 동안 [The Pirates] 는 귀하에게 특정 개인 식별 정보를 제공하도록 요구할 수 있습니다. [그러나 현재는 테스트및 오류 해결을 위한 기기 정보 외의 개인정보를 요구하지 않습니다.]

[정보수집] 요청은 저희가 보관하고 본 개인 정보 보호 정책에 설명 된대로 사용 됩니다. 앱은 귀하를 식별하는 데 사용되는 정보를 수집 할 수 있는 제 3 자 서비스를 사용합니다. 앱에서 사용하는 제 3 자 서비스 제공 업체의 개인 정보 보호 정책 링크로는 다음을 이용할 수 있습니다.

1. Google Play 서비스

2. 로그 데이터

3. 테스트 및 운영에 관한 사용자 정보 데이터

[The Pirates] 는 귀하가 [The Pirates] 서비스를 이용할 때마다 앱에 오류가 발생하는 경우 [The Pirates] 가 귀하의 휴대 전화에서 Log Data 라는 데이터 및 정보 (타사 제품을 통해)를 수집하고 있음을 알려 드리고자 합니다. 이 로그 데이터에는 귀하의 장치 인터넷 프로토콜 ("IP") 주소, 장치 이름, 운영 체제 버전, [The Pirates] 서비스 이용 시 앱 구성, 서비스 사용 시간 및 날짜와 같은 정보가 포함될 수 있습니다.

[쿠키]

쿠키는 일반적으로 익명의 고유 식별자로 사용되는 소량의 데이터가 포함 된 파일입니다. 방문한 웹 사이트에서 브라우저로 전송되며 기기의 내부 메모리에 저장됩니다. 이 서비스는 이러한 "쿠키"를 사용하고 있으며 사용되는 데이터는 유저가 마지막으로 접속한 앱서비스 내의 [MAP 정보]에 해당합니다. 그리고 앱은 정보를 수집하고 서비스를 개선하기 위해 "쿠키"를 사용하는 제 3 자 코드 및 라이브러리를 사용할 수 있습니다. 귀하는 이러한 쿠키를 수락하거나 거부 할 수 있으며 쿠키가 귀하의 장치로 전송되는시기를 알 수 있습니다. 쿠키를 거부하는 경우이 서비스의 일부를 사용하지 못할 수 있습니다.

[서비스 제공자]

[The Pirates] 는 다음과 같은 이유로 제 3 자 회사 및 개인을 고용 할 수 있습니다. 1. 서비스를 용이하게하기 위함 2. 당사를 대신하여 서비스를 제공하기 위함 3. [The Pirates] 관련 서비스 수행 위함 4. 당사의 서비스 사용 방식을 분석하기 위함 [The Pirates] 는 이러한 제 3자가 귀하의 개인 정보에 액세스 할 수 있음 을 이 서비스의 사용자에게 알리고 있습니다. [The Pirates] 은 위에서 말한 사항 외에 다른 목적으로 정보를 공개하거나 사용하지 않습니다.

[보안]

[The Pirates]는 귀하의 개인 정보 제공에 대한 귀하의 신뢰를 소중히 여기므로 당사는이를 보호하기 위해 상업적으로 허용되는 수단을 사용하기 위해 노력하고 있습니다. 그러나 인터넷을 통한 전송 방법이나 전자 저장 방법은 100 % 안전하고 신뢰할 수 없으며 [The Pirates]는 절대적인 보안을 보장 할 수 없습니다.

[다른 사이트로의 링크]

이 서비스에는 다른 사이트에 대한 링크가 포함될 수 있습니다. 타사 링크를 클릭하면 해당 사이트로 이동합니다. 이러한 외부 사이트는 [The Pirates]에서 운영하지 않습니다. 따라서 [The Pirates]는 이러한 웹 사이트의 개인 정보 보호 정책을 검토 할 것을 강력히 권고합니다. [The Pirates]는 제 3 자 사이트 또는 서비스의 콘텐츠, 개인 정보 보호 정책 또는 관행에 대해 통제 할 수 없으며 책임을지지 않습니다.

[아동의 프라이버시]

[The Pirates] 는 13 세 미만의 아동으로부터 개인 식별 정보를 고의로 수집하지 않습니다. [The Pirates] 가 13 세 미만의 아동이 [The Pirates]를 제공 한 사실을 발견 한 경우 개인 정보와 함께 [The Pirates] 는 이를 당사 서버에서 즉시 삭제합니다. 귀하가 부모 또는 보호자이고 귀하의 자녀가 당사에 개인 정보를 제공 한 것을 알고있는 경우 [나 / 우리]가 필요한 조치를 취할 수 있도록 [The Pirates]에 연락하십시오.

[개인 정보 보호 정책의 변경]

[The Pirates]는 수시로 개인 정보 보호 정책을 업데이트 할 수 있습니다. 따라서 변경 사항이 있는지이 페이지를 주기적으로 검토하는 것이 좋습니다. [The Pirates] 는 이 페이지에 새로운 개인 정보 보호 정책을 게시하여 변경 사항을 알려 드릴 것입니다.

[문의하기]

[The Pirates] 개인 정보 보호 정책에 대한 질문이나 제안이 있으시면 언제든지 [The Pirates] (dslcltn@gmail.com)로 연락하십시오.

 

요즘 aws ec2 docker nginx svelteKit 이렇게 해보고 있는데

일단 build 경로에 대해 이야기 해보자

빌드경로가 다른 블로그나 사이트에선 /app/build에 있다는데 왜 안나올까 한다

일단 svelte.config.js 파일에서

import adapter from '@sveltejs/adapter-auto';

이렇게 되어 있으면 별도로 빌드 경로를 지정해 주지 않는이상 .svelte-kit 에만 빌드 파일이 

만들어 지는것 같다.

따로 경로 지정해 줘도 안먹히는거 같고

그러다가 nginx 한번에 연결시키는건 포기하고 

사실 프록시 할필요도 딱히 없고 해서 단독으로 올려보려고 하는데

node로 올리면 된다는 글을 보았다. 

그런데

node build/index.js 뭐 이런명령어를 치라는데

안먹힌다

일단 뭐가 문제일까 생각해보니

build 경로도 없고 index.js 파일은 .svelte-kit 폴더의 아웃풋 안에 서버폴더 안에 있었다.

다시 여기저기 작은 svelte 생태계를 찾아 보니

svelte.config.js 파일의 어댑터를 노드로 설정을 해야 한다고 한다

그래 어댑터가 오토로 되어 있어서 자동으로 막 해준다고 하는데 

이젠 명확히 어떤 서버에 올릴지 목적이 생겼지 않은가 svelteKit 프로젝트를 node서버에 올리기 위해

npm i -D @sveltejs/adapter-node 

이 명령어로 노드어댑터 설치 해주고 

svelte.config.js 파일 상단에 

import adapter from '@sveltejs/adapter-auto';
->
import adapter from '@sveltejs/adapter-node';
 
이렇게 바꿔주니 
 
npm run build 를 실행시켰을대 bulid 폴더가 생기면서
거기에 빌드 파일이 생성되고
 
index.js 파일도 빌드경로에 바로 생성이 되는것을 확인 할 수 있었다.
 
 

 

 

 

 

aws ec2 환경에서 gitaction이랑 docker을 사용해 

svelteKit 으로 된 프로젝트를 도커로 빌드해서 ecr에 푸시하고 ec2에 npm run dev 로  CI/CD로

띄우는것까지는 작업을 하고

이제 nginx를 붙여볼까 하면서 도커파일좀 건드려 보고 있는데

검색해서 나온 것들은 

엔진스에 스벨트 빌드해서 나온 아티팩트를 서빙하려고

COPY --from=build /app/public /usr/share/nginx/html

이렇게 해뒀는데 

 

COPY failed: stat /app/public: file does not exist
Error: Process completed with exit code 1.

이런 에러가 나온다

도커 빌드 할때 저 위치를 찾을수 없다고 나오는데 한참을 헤매다가 

빌드파일은 .svelte-kit에 있다는걸 알아냈다

그러나

COPY --from=build /app/.svelte-kit /usr/share/nginx/html

으로 수정을 하더라도 도커 빌드는 되지만 nginx에서는 게이트웨이 오류가 난다.

스벨트가 따로 index.html 파일을 생성하지 않기 때문이다.

 

아마 nginx를 사용하려면 컨테이너 2개 띄워서

nginx->svlete로 프록시해주는 구조로 가야 할것 같다.

 

괜히 한큐에 끝내보려고 아둥바둥 시간 많이 썻다.

 

 

 

svelteKit으로 만든 프로젝트를 개발환경 docker compose 에서 올려보려고 하는데

페이지가 작동되지 않는다는 메세지만 나타난다?

포트설정도 제대로 했고 도커에서 정상적으로 올라간것도 확인 했는데

뭐가 문제인지를 몰라 한참을 헤맸다

 

아래는 svelteKit 프로젝트에 만든 도커 파일이다.

 

1. DockerFile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Node.js 18 버전을 사용합니다.
FROM node:18
 
# 앱 디렉터리를 만듭니다.
WORKDIR /app
 
# 앱 소스 코드를 복사합니다.
COPY . .
 
# 앱 종속성 설치를 실행합니다.
RUN npm install
 
# 앱을 빌드합니다.
RUN npm run build
 
 
# 앱 실행을 위한 명령어를 지정합니다.
CMD ["npm""run""start"]
cs

 

 

2. docker-compose.yml 파일

1
2
3
4
5
6
7
8
9
version: "3.9"
services:
  app:
    build: .
    ports:
      - "5173:5173"
    environment:
      NODE_ENV: production
 
cs

 

?? 뭐 문제가 없어 보인다. 그리고 저 문제로 검색을 해봐도

nginx관련된 내용만 잔뜩 나온다 난 nginx 안쓰는데!

그러다 한 문서를 발견했다.

 

https://github.com/sveltejs/kit/discussions/5673

 

dockerize sveltekit · sveltejs/kit · Discussion #5673

I have an issue with running sveltekit app through the docker container. I am able to run an app in docker but when I want to make some changes on frontend UI I can see them only for one second. An...

github.com

 

문제는

CMD npm run dev

이부분이었다.

이걸 

CMD npm run dev -- --host 

이런식으로 써줘야 한단다.

난 dev대신 start 스크립트를 따로 만들었으니 

도커파일을 수정하면

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Node.js 18 버전을 사용합니다.
FROM node:18
 
# 앱 디렉터리를 만듭니다.
WORKDIR /app
 
# 앱 소스 코드를 복사합니다.
COPY . .
 
# 앱 종속성 설치를 실행합니다.
RUN npm install
 
# 앱을 빌드합니다.
RUN npm run build
 
 
# 앱 실행을 위한 명령어를 지정합니다.
CMD ["npm""run""start""--""--host"]
 
 
cs

 

이렇게 수정을 해줘야 하는 것이었다.

한참동안 뭔가 문제인지 몰랐는데 문제점을 찾아서 다행이다.

이 블로그를 찾아오는 다른 사람들도 이와같은 문제였다면 잘 해결했길 바란다.

요즘 svelte랑 tailwind css 사용해서 조금씩 뭔가 만들어 보고 있는데

드롭다운 메뉴를 만들어 보려고 하던 와중

document is not defined 이런 에러가 나타났다.

문제가 되었던 코드는

1
2
3
4
5
6
7
$: {
    if (isOpen) {
      document.addEventListener('click', handleClickOutside);
    } else {
      document.removeEventListener('click', handleClickOutside);
    }
  }
cs

이 부분이었다

 

이 에러는 JavaScript가 브라우저 내에서 실행되는 환경에서만 document 객체가 정의되기 때문에

 Node.js 환경에서 실행될 때 발생하는 에러였다 SvelteKit 프로젝트에서는 서버에서 실행 중인

노드 코드에서는 document 객체를 사용할 수 없기 때문이었다.

그래서 스택오버플로우를 찾아보니 onMount를 사용해 해결 하는 방법이 있었다

 

1
2
3
4
5
6
7
8
9
10
11
import { onMount } from 'svelte';
 
$: {
    if (isOpen) {
      document.addEventListener('click', handleClickOutside);
    } else {
        onMount(() => {
            document.removeEventListener('click', handleClickOutside);
        });
    }
  }
cs

 

이렇게 온마운트 안에 다큐먼트를 사용하는 코드를 넣으면 해결이 된다.

onMount는 컴포넌트가 처음으로 DOM에 렌더링 될 때 실행되는 함수이다.

아마 위 코드를 사용할때 다큐먼트가 정의되지 않은 상태에서 에러가 발생하는듯 하다.

스벨트의 라이프 싸이클에 대해서 나중에 한번 정리를 해봐야겠다.

 

 

+ Recent posts