스토리북 파일 안에서 Typescript에서 설정한 절대 경로로 Import를 할 때 경로를 찾지 못하는 오류가 발생한다.

 

그럴 때는 tsconfig-paths-webpack-plugin을 설치하고 사용하면 된다.

 

.storybook/main.ts에서 tsconfig-paths-webpack-plugin를 불러와주고

const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');

 

config 객체 안에 코드를 넣어준다.

const config: StorybookConfig = {
	... 생략 ...
    
	webpackFinal: async (config: Configuration) => {
        if (!config.resolve) {
            config.resolve = {};
        }

        if (!config.resolve.plugins) {
            config.resolve.plugins = [];
        }

        config.resolve.plugins.push(new TsconfigPathsPlugin({}));

        return config;
    }
}
 
 

 

 

반응형

다수의 class를 선택자(selector)로 사용했을 때 헷갈리는 부분을 정리해볼까 한다.

.first-class.second-class {
	background: red;
}

두개 이상의 class를 공백 없이 넣게 되면 두개의 클래스를 모두 가지고 있는 요소가 선택된다.

<div class="first-class second-class"></div>

 

 

.first-class, .second-class {
	background: red;
}

두개 이상의 class를 쉼표(comma)로 구분할 경우 class가 포함된 모든 요소를 선택하게 된다. 

<div class="first-class"></div>

<div class="second-class"></div>

 

 

.first-class .second-class {
	background: red;
}

두개 이상의 class를 공백으로만 구별할 경우 앞에 class의 하위 요소를 선택하게 된다.

<div class="first-class">

    <div class="second-class"></div>

    <div class="second-class"></div>

</div>

 

.first-class + div {
	background: red;
}

더하기로 구분할 경우 첫번째 요소의 가장 인접한 형제 요소를 선택하게 된다.

<div class="first-class">

   <div></div>

   <div></div>

</div>

 

.first-class > div {
	background: red;
}

> 를 사용하여 구분할 경우 바로 하위에 있는 자식 요소들을 선택하게 된다.

<div></div>

<div class="first-class">

    <div></div>

    <div></div>

</div>

<div></div>

 

 

.first-class ~ div {
	background: red;
}

~ 을 사용할 경우 첫번째 class의 형제 요소들을 선택하게 된다.

<div class="first-class"></div>

<div></div>

<div></div>

 

 

반응형

Next.js는 node_modules의 일부 파일을 포함하여 프로덕션 배포에 필요한 파일만 복사하는 독립 실행형 폴더를 자동으로 생성할 수 있다.

 

이 기능을 사용하기 위해서는 next.config.js에서 아래와 같이 활성화를 해주면 된다.

module.exports = {
  output: 'standalone',
}

이렇게 하면 .next/standalone에 폴더가 생성되며, 이 폴더는 node_modules를 설치하지 않고도 자체적으로 배포할 수 있다.

 

또한 next start 대신 사용할 수 있는 server.js 파일도 생성된다. 이 서버는 기본적으로 public 또는 .next/static 폴더를 복사하지 않는다. 이러한 폴더는 CDN에서 처리하는 것이 이상적이지만, 이러한 폴더를 standalone/publicstandalone/.next/static 정적 폴더에 수동으로 복사할 수 있으며, 그 후에는 server.js 파일이 자동으로 해당 폴더를 서비스한다.

 

운영 서버에 npm run build를 통해 생성된 .next/standalone 폴더만 업로드 하면 되고 node server.js로 실행하면 된다. 

 

추가적으로 .next/static을 CDN으로 처리를 하려 한다. 

next.config.js에서 assetPrefix 옵션에 CDN의 URL을 추가해주면 CDN 안에서 .next/static 를 찾게 된다.

 

module.exports = {
    output: 'standalone',
    reactStrictMode: false,
    swcMinify: true,
    assetPrefix: 'https://cdn.mydomain.com'
};

아래 보면 cdn 폴더 경로를 보면 .next가 아닌 _next로 찾게 되기 때문에 폴더명을 변경해줘야한다.

 

반응형

Next.js에서 단위 테스트를 하기 위해서 공식 문서에서 설명하는 Jest와 React Testing Library를 사용했다.

 

먼저 4개의 라이브러리를 설치한다. jest, jest-environment-jsdom, @testing-library/react, @testing-library/jest-dom

npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom

 

루트 경로에 jest.config.mjs 파일을 생성하여 아래와 같이 내용을 추가해준다.

import nextJest from 'next/jest'
 
const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './',
})
 
// Add any custom config to be passed to Jest
/** @type {import('jest').Config} */
const config = {
  // Add more setup options before each test is run
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
 
  testEnvironment: 'jest-environment-jsdom',
}
 
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
export default createJestConfig(config)

Next.js에서 SWC 컴파일러를 사용하기 때문에 더 이상 설정은 필요 없다.

 

버튼 컴포넌트의 테스트 코드를 작성해봤다.

import { render } from '@testing-library/react';

import Button from './index';

it('button component snapshot', () => {
    const buttonRender = render(
        <Button type="button" onClick={() => console.log('button onClick')}>
            Test Button
        </Button>
    );

    expect(buttonRender.container).toMatchSnapshot();
});

테스트 코드도 잘 통과 됐고 아래와 같이 스냅샷도 생성이 됐다.

 

마지막으로 package.json에서 test 스크립트를 추가 했다.

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "test": "jest --watch"
  }
}

 

웹스톰에서는 동작에는 문제 없지만 코드에 문제 있다는 경고를 표시했다. @types/jest를 설치해주니 말끔하게 해결했다.

npm i --save-dev @types/jest

 

반응형

Next.js를 사용하면서 정적페이지로 배포하는 것이 아니기 때문에 서버에 배포하는 과정에서 도커를 사용하기로 하였고 코드를 dev, stage, main에 병합이 되는 순간 AWS의 ECR에 이미지가 생성이 되도록 하였습니다.

 

그 과정에서 환경변수(.env)를 어떻게 관리 해야되나 고민하였습니다. Repo에 민감한 정보가 있는 .env를 올리는 것도 아닌거 같고 서버 안에서 .env 생성하여 컨테이너가 그 환경변수를 바라보게 하는 것도 뭔가 좋아보이지 않았습니다. (방법을 모르는 것도 있습니다)

 

그래서 생각한 방법이 Github Action에서 도커 이미지를 생성할 때 옵션 값으로 환경변수(.env)를 넣기로 하였고 방법을 찾아봤습니다.

 

맞는 방법인지는 모르겠지만 CI/CD를 하는 방법을 아래와 같은 흐름으로 진행하기로 했습니다.

 

먼저 Github Action에서 Secrets를 설정하는 방법을 살펴보면 Settings -> Security -> Secrets and variables -> Actions에서 만들 수 있습니다.

브랜치별로 환경변수를 다르게 할 필요가 없다면 Repository secrets에 환경변수를 등록하면 되지만, 저는 main, stage, dev의 환경변수를 다 다르게 설정을 해줘야했습니다. 그래서 Environment secrets을 활용하기로 합니다.

 

환경에 대한 이름을 지어주고 

아래와 같이 Selected branches를 해서 원하는 브랜치 명을 입력해줍니다.

아래에서 Add secret을 입력해줍니다. 

이런 방식으로 dev, stage, main에 환경변수명과 값을 넣어주면 됩니다. 이제 이 secrets 값을 github action 스크립트 파일(yml)에서 도커로 주입을 해줘야합니다. 환경별로 다른 secrets을 넣기 위해서는 어떤 환경인지 선언을 해줘야합니다.

jobs:
    build:
        environment: dev // main 혹은 stage
 
run: |
  docker build \
  --build-arg "NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL}}" \
  --build-arg "NEXT_PUBLIC_API_KEY=${{ secrets.NEXT_PUBLIC_API_KEY}}" \
  --build-arg "NAVER_CLIENT_ID=${{ secrets.NAVER_CLIENT_ID}}" \
  --build-arg "KAKAO_REST_API_KEY=${{ secrets.KAKAO_REST_API_KEY}}" \
  --build-arg "NEXT_PUBLIC_TOSS_CLIENTKEY=${{ secrets.NEXT_PUBLIC_TOSS_CLIENTKEY}}" \
  --build-arg "NEXT_PUBLIC_ORIGIN_URL=${{ secrets.NEXT_PUBLIC_ORIGIN_URL}}" \
  --build-arg "NEXT_PUBLIC_TEST_API_URL=${{ secrets.NEXT_PUBLIC_TEST_API_URL}}" \
  -t $ECR_REPOSITORY:$IMAGE_TAG .

Dockerfile을 통해서 빌드명령어를 실행 시킬 때 --build-arg에 다음과 같이 secrets을 넣어줍니다. 앞에서 environment: dev 로 설정해줬기 때문에 github에서 dev 환경에 해당하는 secrets 값이 들어가게 됩니다.

 

이제 Dockerfile이 실행되고 --build-arg로 넘겨줬던 변수를 받아서 env로 넣는 과정이 필요합니다. DockerFile 안에 다음과 같이 넣었습니다.

ARG NEXT_PUBLIC_API_URL
ARG NEXT_PUBLIC_API_KEY
ARG NAVER_CLIENT_ID
ARG KAKAO_REST_API_KEY
ARG NEXT_PUBLIC_TOSS_CLIENTKEY
ARG NEXT_PUBLIC_ORIGIN_URL
ARG NEXT_PUBLIC_TEST_API_URL

ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
ENV NEXT_PUBLIC_API_KEY=${NEXT_PUBLIC_API_KEY}
ENV NAVER_CLIENT_ID=${NAVER_CLIENT_ID}
ENV KAKAO_REST_API_KEY=${KAKAO_REST_API_KEY}
ENV NEXT_PUBLIC_TOSS_CLIENTKEY=${NEXT_PUBLIC_TOSS_CLIENTKEY}
ENV NEXT_PUBLIC_ORIGIN_URL=${NEXT_PUBLIC_ORIGIN_URL}
ENV NEXT_PUBLIC_TEST_API_URL=${NEXT_PUBLIC_TEST_API_URL}

 

코드가 자동으로 병합 됐을 때 다음과 같이 실행되는걸 볼 수 있습니다.

 

브랜치 별로 환경변수를 다르게 입력하고 싶었지만 yml 파일에서 환경변수를 입력하는 부분을 찾지 못해서 오랜 시간이 걸렸던 것 같습니다. 

jobs:
    build:
        environment: dev

이 부분만 빠르게 찾았다면 쉽게 했을 작업인데 한참 걸렸습니다. 이런 방법으로 도커를 배포하는게 좋은 방법인지는 더 찾아봐야되지만 Github Action에서 브랜치별로 secrets를 주입하는 기능에 대해서 이해하고 활용했다는 점에서 경험치를 쌓았다고 생각합니다.

 
 

 

 

반응형

'기타' 카테고리의 다른 글

이클립스 자주 쓰는 단축키  (0) 2016.09.04
소스코드에 색(Syntax)을 입혀주는 사이트  (0) 2016.01.13

프로젝트를 생성하고 splash를 만드는데 필요한 것들을 자동으로 만들어주는 react-native-make 모듈을 설치합니다.

react-native-make 모듈을 통해 이미지를 추가하고 react-native-splash-screen 모듈을 설치하여 splash를 컨트롤 합니다.

 

 

1. npm i -D @bam.tech/react-native-make

 

2. splash에 쓰일 이미지를 프로젝트 내에 경로에 저장

 

3. 명령어 입력 - react-native set-splash --path ./Assets/images/splash.png --resize cover --background "#FFFFFF"

 

4. npm i react-native-splash-screen --save

 

5. pod install

 

6. ios -> AppDelegate.m 에서 소스코드 추가 

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "RNSplashScreen.h"  // here

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // ...other code

    [RNSplashScreen show];  // here
    // or
    //[RNSplashScreen showSplash:@"LaunchScreen" inRootView:rootView];
    return YES;
}

@end

 

 

7. android -> MainActivity.java 에서 소스코드 추가

import android.os.Bundle; // here
import com.facebook.react.ReactActivity;
// react-native-splash-screen >= 0.3.1
import org.devio.rn.splashscreen.SplashScreen; // here
// react-native-splash-screen < 0.3.1
import com.cboy.rn.splashscreen.SplashScreen; // here

public class MainActivity extends ReactActivity {
   @Override
    protected void onCreate(Bundle savedInstanceState) {
        SplashScreen.show(this);  // here
        super.onCreate(savedInstanceState);
    }
    // ...other code
}

 

 

8. app.js에서 componentDidMount 안에 SplashScreen.hide(); 추가

import React from 'react';
import { Text } from 'react-native';
import SplashScreen from 'react-native-splash-screen'

export default class App extends React.Component {
  componentDidMount() {
    SplashScreen.hide();
  }

  render() {
    return (
      <Text>hello world</Text>
    )
  }
}

 

반응형

expo에서는 폰트를 추가하기가 쉽지만 expo를 쓰지 않는다면 안드로이드, iOS 따로 설정해줘야합니다.

 

먼저 iOS의 경우부터 보겠습니다.

프로젝트 폴더 안에서 /ios/projectName.xcodeproj 파일을 실행시켜서 xCode를 킵니다. 미리 Fonts라는 폴더를 생성하고 그 안에 추가할 폰트를 넣어놓습니다.

프로젝트에서 오른쪽 버튼으로 Add Files to "project name".. 를 클릭하고 만들어놓은 폴더를 추가해주고 Add를 눌러줍니다.

프로젝트를 누르고 Info 탭으로 가서 Fonts provided by application를 추가해주고 Item에 폰트명을 적어줍니다.

여기까지가 iOS에 폰트 추가하는 방법입니다.

 

안드로이드에서 폰트 추가하는 방법을 보겠습니다.

안드로이드는 iOS처럼 복잡하지 않고 프로젝트에 폰트를 추가하는 것으로 끝이납니다.

먼저 프로젝트에서 /android/app/src/main/assets/fonts에서 폴더를 생성하고 폰트를 추가해주는 것으로 끝이납니다.

 

폰트 추가하는 것은 끝이 났고 어느 곳이든 스타일 태그에 폰트 확장자를 제외하고 사용하면 됩니다.

저 같은 경우에는 Font.js라는 파일을 만들어서 조금 더 줄여서 폰트를 사용합니다.

 

이런식으로 폰트를 사용합니다.

 

iOS에서 xCode를 사용해서 폰트 추가하는 방법만 불편하고 나머지 사용방법은 편한거 같아서 expo와 큰 차이는 없는거 같습니다.

 

반응형

깨진 QR코드

 

 

 

 

 

 

 

 

QR코드 발급하는 모듈로 react-native-qrcode-svg를 선택하여 프로젝트를 진행하는 과정에서 랜덤하게 QR코드 이미지가 깨지면서 보이는 현상이 발견되었다.

주로 안드로이드에서 발생하였고 iOS에서도 매우 드물게 발생하였다. 또 열심히 구글링 하는 도중 모듈에 문제가 있는 것을 발견하였고 해결책을 찾았다.

 

먼저 아래 경로로 이동한다.

/node_modules/react-native-qrcode-svg/src/index.js

 

index.js 파일에서 141번째 줄에 있는 소스코드에 strokeLinecap={'square'} 코드로 변경해준다.

<Path
  d={path}
  strokeLinecap={'square'}
  stroke={enableLinearGradient ? 'url(#grad)' : color}
  strokeWidth={cellSize}
/>

 

다음은 ./src/tranformMatrixIntoPath.js 파일로 이동하여 2번째 줄과 9번째 줄의 소스코드를 변경한다.

2번째 줄
const cellSize = size / matrix.length - 0.1   
9번째 줄
path += `M${cellSize * j + 6} ${cellSize / 2 + cellSize * i}`

 

변경한 후에 테스트 해본 결과 완벽하게 작동되지는 않았지만 QR코드 인식에는 문제가 없는 것으로 확인됐다.

 

 

https://github.com/awesomejerry/react-native-qrcode-svg/issues/108

 

반응형

* IOS에서는 가상 기기를 시뮬레이터(simulator)라고 하고 Android에서는 가상기기를 에뮬레이터(emulator)라고 합니다.

 

Expo를 버리고 React Native만으로 처음하는 프로젝트에서부터 IOS 시뮬레이터에서는 정상작동하던 프로젝트가 Android에서는 오류를 내면서 정상작동하지 않는 상황이 발생했습니다. 에뮬레이터가 정상적으로 실행되지만 에뮬레이터 안에 앱이 설치가 되지 않으면서 에러가 발생했는데요. 안드로이드 환경설정이 잘못된 것으로 판단되서 계속 세팅을 확인하면서 시간을 낭비하게 됐습니다.

error Failed to install the app. Make sure you have the Android development 
environment set up: https://reactnative.dev/docs/environment-setup. 
Run CLI with --verbose flag for more details.
Error: Command failed: ./gradlew app:installDebug -PreactNativeDevServerPort=8081

 

계속 되는 구글링 결과 gradle이 문제라는 것을 확인했습니다.

 

React Native 프로젝트 안에 /android/gradle/wrapper/gradle-wrapper.properties 파일로 이동하여 distributionUrl로 시작하는 라인에서 gradle 버전을 변경해주고 재시작하면 오류 없이 정상작동하는 것을 확인할 수 있습니다.

 

아래와 같은 버전을

distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip

다음과 같이 변경합니다.

distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip

 

출처 : https://stackoverflow.com/questions/60844245/how-solve-could-not-initialize-class-org-codehaus-groovy-reflection-reflectionc/61913747#61913747

반응형

JSTL core는 기본적인 기능들을 구현해 놓은 라이브러리


<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>


출력 : <c:out>

조건 : <c:if>, <c:choose>, <c:when>, <c:otherwise>

반복 : <c:forEach>, <c:forTokens>

예외 : <c:catch>

변수 설정 및 삭제 : <c:set>, <c:remove>



<c:out> (출력시키는 태그)

<c:out value="출력값" default="기본값" excapeXml="true or false">


<c:if> 

<c:if test="조건" var="변수명" scope="범위">


<c:choose> (switch와 비슷한 역할을 함. 별다른 의미 없이 조건문의 시작을 알림)

<c:choose>
    <c:when test="조건"></c:when>
    <c:otherwise></c:otherwise>
</c:choose>


<c:forEach> (items 속성에 컬렉션이나 배열 형태의 객체를 지정하여 객체의 인덱스만큼 반복할 수도 있음)

<c:forEach items="객체명" begin="시작 인덱스" end="끝 인덱스" step="증감식" var="변수명" varStatus="상태변수">


<c:forTokens>

<c:forTokens items="객체명" delims="구분자" begin="시작 인덱스" end="끝 인덱스" step="증감식" var="변수명" varStatus="상태변수">


<c:catch>

<c:catch var="변수명">


<c:set> (지정된 변수에 값을 설정하는 태그)

<c:set var="변수명" value="설정값" target="객체" property="값" scope="범위">


<c:remove> 

<c:remove var="변수명" scope="범위">




JSTL fmt

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

인코딩 : <fmt:requestEncoding>

국제화 : <fmt:setLocale>, <fmt:timeZone>, <fmt:setTimeZone>, <fmt:bundle>, <fmt:setBundle>, <fmt:message>, <fmt:param>

형식화 : <fmt:formatNumber>, <fmt:parseNumber>, <fmt:formatDate>, <fmt:parseDate>



<fmt:requestEncoding> (Request 객체로부터 전달 받은 값들을 인코딩할때 사용)

<fmt:requestEncoding value="인코딩값">



<fmt:setLocale> (다국어 페이지를 사용할 때 언어를 지정하는 태그. value 속성은 어떤 언어를 사용할지, variant 속성은 브라우저의 스펙)

<fmt:setLocale value="값" variant="" scope="범위">


<fmt:setTimeZone> (지정한 지역 값으로 시간대를 맞추는 기능, <fmt:timeZone>의 경우 첫 태그와 끝 태그 사이의 영역만 적용. setTimeZone은 페이지 전체에 영량을 줌.

<fmt:setTimeZone value="값" var="변수명" scope="범위">



반응형

'JSP & Spring' 카테고리의 다른 글

jstl에서 비교문 [펌]  (0) 2016.09.20
스프링 외부 경로 폴더 지정하기  (0) 2016.09.12
@ModelAttribute, @RequestParam  (0) 2016.09.08
스프링 초기환경세팅  (0) 2016.09.04
utf-8 인코딩.  (0) 2016.09.01

+ Recent posts