[컴] spring에서 test container 사용시간 단축하기

 

test container / testcontainer / 스프링에서 / 스프링부트 / springboot / 스프링

spring에서 test container 사용시간 단축하기

test container 를 이용해서 test 를 하면, container 를 load 하는 시간이 오래걸린다. 개인적으로 ssd 가 아닌 hdd 에서 동작하도록 해서 그런지 더 실행시간이 오래걸렸다.

그래서 test 를 자주 하려면, 최대한 test container 의 실행을 줄이는 것이 좋다. 즉, 덜 자주 unload , load 를 하는 것이 낫다.

container 를 1번만 실행

Testcontainer 를 사용하면 test class에 @Testcontainers를 사용하게 된다. 이것은 매 class마다 container 를 실행하게 된다.

대체로 test container 를 이용해서 test 를 하면, container 를 load 하는 시간이 오래걸린다. 개인적으로 ssd 가 아닌 hdd 에서 동작하도록 해서 그런지 더 실행시간이 오래걸렸다.

때문에, test 를 자주 하려면, 최대한 test container 의 실행을 줄이는 것이 좋다. 즉, 덜 자주 unload , load 를 하는 것이 낫다.

그래서 이것을 우리는 이것을 전체 test 에서 1번만 실행하게 할 것이다.

위 글에도 나와 있지만 static variable 에 할당하는 것으로 container 의 생성을 1번으로 만든다.

datasource 의 설정

이것은 ApplicationContextInitializer 를 이용한다.

특정 profile 에서는 현재 띄워져있는 container 를 사용하도록 한다

testcontainer를 부하를 줄이려 1번만 load 한다고 해도, TDD 등을 할때는 속도가 엄청 느리다. 계속 unload/load 가 이뤄지기 때문에 빠른 개발이 어렵다.

그래서 build 시점에는 testcontainer 를 사용해서 test 를 하지만, 개발을 할때는 현재 띄워져 있는 container에 접속해서 test 할 수 있도록 했다.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(initializers = TestcontainersInitializer.class)
class FishApplicationTests {
}
...


public class TestcontainersInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    private static final String MIGRATION_LOCATION = "filesystem:db/migration";
    public static final MariaDBContainer<?> mariaDB = new MariaDBContainer<>("mariadb:10.6")
            .withDatabaseName("myapp")
            .withUsername("myapp-user")
            .withUrlParam("useBulkStmts", "false")
            .withPassword("strong-password")
            .withReuse(true);

    public static final LocalStackContainer localStack = new LocalStackContainer(
            DockerImageName.parse("localstack/localstack"))
            .withServices(LocalStackContainer.Service.S3)
            .withReuse(true);

    public static final KafkaContainer kafka = new KafkaContainer(
            DockerImageName.parse("confluentinc/cp-kafka:7.5.0"))
            .withReuse(true);

    /*
     * Static block runs before Spring Boot starts the application context.
     */

    @Override
    public void initialize(@NotNull ConfigurableApplicationContext ctx) {
        // if system.property active profile is test, do not start containers
        final String EXCLUDE_PROFILE = "testnocontainer";
        var activeProfiles = ctx.getEnvironment().getActiveProfiles();
        if (activeProfiles.length <= 0 || !activeProfiles[0].equals(EXCLUDE_PROFILE)) {
            // if active profile is NOT 'testnocontainer'

            // 만약 여러개의 test class 를 실행하면 이부분은 당연히 여러번 실행된다. 하지만 괜찮다.
            // static 이라서 여러번 실행되지 않았다.

            /**
             * This routine runs multiple times, but due to the use of the static variables,
             * it only works once
             */
            /*
             * deepStart() is used to start containers in parallel.
             */
            Startables.deepStart(mariaDB, localStack, kafka).join();
            
            runMigrations(); // run flyway

            // 여기를 실행하지 않는 profile 은 application.xml 의 datasource 쪽을 바라보게 된다.
            // 그러므로 datasource setting 을 해놔야 한다.
            TestPropertyValues.of("spring.datasource.url=" + mariaDB.getJdbcUrl())
                .and("spring.datasource.username=" + mariaDB.getUsername())
                .and("spring.datasource.password=" + mariaDB.getPassword())
                .and("spring.kafka.bootstrap-servers=" + kafka.getBootstrapServers())
                .applyTo(ctx);
        }
    }

    private static void runMigrations() {
        Flyway.configure()
                .dataSource(mariaDB.getJdbcUrl(), mariaDB.getUsername(),
                        mariaDB.getPassword())
                .locations(MIGRATION_LOCATION)
                .schemas(mariaDB.getDatabaseName())
                .baselineOnMigrate(true)
                .load().migrate();
    }
}

See Also

  1. 쿠…sal: [컴] Spring Test 에서 @Sql 을 code 로

[컴] AI 에서 어떻게 model 을 디자인 하는 것일까?

 

AI 에서 어떻게 model 을 디자인 하는 것일까?

아래 영상을 보고 간단하게 keras 를 이용해서 ai model 을 만들어볼 수 있다.

  • Make Your First AI in 15 Minutes with Python - YouTube
  • 에러
    • ValueError: Input 0 of layer "sequential_1" is incompatible with the layer: expected shape=(None, 455, 30), found shape=(None, 30) 와 같은 에러가 발생하는데 code에서 model.add(tf.keras.layers.Dense(256, input_shape=x_train.shape[1:], activation='sigmoid')) 로 변경하면 된다. x_train.shape 대신 x_train.shape[1:]를 사용하는 것이다.

여기서 궁금중은 그러면 model 을 만들때 사용한 layer를 어떤 기준으로 추가했느냐이다. 그리고 그 layer에 사용한 activation function 은 어떤 기준으로 선택했는가 이다.

model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Dense(256, input_shape=x_train.shape, activation='sigmoid'))
model.add(tf.keras.layers.Dense(256, activation='sigmoid'))
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))

그럼 어떻게 model 을 만들까?

이것에 대한 대답을 다음 글에서 확인할 수 있을 듯 하다.

이글은 deep learning 의 성능을 향상시키는 방법을 이야기하는데, 여러가지 방법이 있다.

  • 데이터양을 증가시키는 방법
  • 데이터를 normalize 하는 법
  • 데이터를 시각화해서 모양을 봐보라, 그러면 이것이 가우스분포같은지, log분포같은지등을 보고, 필요한 변환을 해볼 수 있다.
  • feature selection
  • 문제를 구성하는 방법을 변경해봐라
  • 여러 알고리즘을 통한 향상
    • 선택한 알고리즘이 best가 아닐 수 있다. linear methods, tree methods, instance methods, 다른 neural network methods 들로 평가해봐라.
    • 논문등 다른 사람들이 했던 비슷한 문제들을 찾아서 아이디어를 얻어라(리서치를 해라)
    • 샘플링한 dataset을 다시 잘 sampling 한다. 예를 들어 train/test 로 나눴다면, train 에 해당하는 dataset이 문제의 대표성을 띠는지 등. 또는 작은 dataset을 사용하고, 그에 맞는 model을 선택해서 테스트해보고 큰 dataset으로 확장해 볼 수도 있다.
  • 알고리즘 튜닝을 통한 향상
    • 진단, overfitting인지 underfitting인지
    • 가중치 초기화
    • 학습률을 조정
    • activation function
    • network topology , 여러가지 구성을 테스트해본다. 1개의 많은 newrons를 갖는 hidden layer를 시도하던지, layer당 적은수의 neuron들을 갖는 deep network 를 시도하던지, 이 2개를 전부해보던지등.
    • batches and Epochs, batch size를 변경하는 것은 gradient를 정의하고, weight들을 얼마나 자주 update하는지를 정한다. epoch는 배치마다 ‘network 에 노출되는 전체 dataset’ 이다.
    • 정규화, overfitting을 억제하는 좋은 방법이다. dropout이라는 정규화기술도 있다.
    • 최정화와 손실, 이전에 최적화는 ‘확률적 경사하강법(stochastic gradient descnet)’ 였지만, 이젠 많은 optimizer들이 있다.
    • 빠른 정지(Early Stopping), 성과가 저하되기 시작하면 학습을 중단한다. 그리고 다른 sample을 만들던지 하는등으로 시간을 절약할 수 있다.

[컴] Docker 의 DockerCli.exe -SwitchDaemon

SwitchDaemon 은 무슨 용도 /

Docker 의 DockerCli.exe -SwitchDaemon

ref. 2 를 보면, linux 에서 windows container 를 실행할 수 없다.

windows 에서는 WSL2 를 제공하기 때문에, linux container 를 실행할 수 있고, windows 이기에 windows container도 실행 가능하다. 그래서 ref. 2 에 보면, linux container와 windows container 를 동시에 실행하는 것도 가능하다.

참고로, Docker for windows에는 windows container를 위해선 WindowsEngine을 사용하고, linux container 를 위해선 LinuxEngine을 사용한다.

engine 변경 방법

이 때 engine 을 변경하는 방법이 DockerCli.exe -SwitchDaemon 이다.

C:\Program Files\Docker\Docker
DockerCli.exe -SwitchDaemon

특정 engine 으로 변환하려면 다음처럼 하면 된다.

DockerCli.exe -SwitchWindowsEngine
DockerCli.exe -SwitchLinuxEngine

References

  1. Run Linux and Windows Containers on Windows 10
  2. docker - Can Windows containers be hosted on Linux? - Stack Overflow
  3. Docker ❤️ WSL 2 - The Future of Docker Desktop for Windows | Docker

[컴] Spring Test 에서 @Sql 을 code 로

springboot test / junit / unittest/ /통합테스트 / .sql / query / 쿼리실행 / 초기 데이터 세팅 / setup / @Sql 대신 사용

Spring Test 에서 @Sql 을 code 로

아래와 같은 annotation 을

@JooqTest
@Sql({"classpath:myquery1.sql", "classpath:myquery2.sql"})
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyTests {
    ...
}

아래처럼 변경했다. 아래는 특정 profile 이 아닐때만 sql 을 실행하도록 했다.

@JooqTest
// @Sql({"classpath:myquery1.sql", "classpath:myquery2.sql"})
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyTests {

    // require: @JooqTest
    @Autowired
    DSLContext ctx;
    @Autowired
    DataSource dataSource;
    @Autowired
    Environment env;

    ProductStatusBgJobProcessor backgroundJobProcessor;

    @BeforeAll
    void beforeAll() {
        runSqlInitData(dataSource);
    }

    @BeforeEach
    void setUp() {

        ...
    }

    @Test
    void myTest1() throws Exception {

        ...

    }

    private void runSqlInitData(DataSource dataSource) {
        final String EXCLUDE_PROFILE = "testnocontainer";
        var activeProfiles = env.getActiveProfiles();
        if (activeProfiles.length <= 0 || !activeProfiles[0].equals(EXCLUDE_PROFILE)) {
            // when not 'testnocontainer' profile
            ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
            populator.addScripts(
                    new ClassPathResource("myquery1.sql"),
                    new ClassPathResource("myquery2.sql"));
            populator.setSeparator(";");
            populator.execute(dataSource);
        }
    }
}

Spring, Integration Test 에서 sql file을 실행하기

sql script 를 실행하기 위해 다음 4개의 함수를 사용할 수 있다.

  • org.springframework.jdbc.datasource.init.ScriptUtils: SQL 스크립트가 parse되고 실행되는 방법을 완전히 제어할 때 사용
  • org.springframework.jdbc.datasource.init.ResourceDatabasePopulator
    • 내부적으로 ScriptUtils 를 사용
  • org.springframework.test.context.junit4.[AbstractTransactionalJUnit4SpringContextTests](https://docs.spring.io/spring-framework/reference/testing/testcontext-framework/support-classes.html#testcontext-support-classes-junit4)
    • 내부적으로 ResourceDatabasePopulator를 사용
  • org.springframework.test.context.testng.[AbstractTransactionalTestNGSpringContextTests](https://docs.spring.io/spring-framework/reference/testing/testcontext-framework/support-classes.html#testcontext-support-classes-testng)
    • 내부적으로 ResourceDatabasePopulator를 사용

Reference

  1. Executing SQL Scripts :: Spring Framework

[컴] SpringBoot v3 에서 openapi v2 사용

javadoc / 자동 문서 / swagger / api 문서 작성 /

SpringBoot v3 에서 openapi v2 사용

gradle 사용시 spring boot 에서 openapi spec의 문서를 만드려면 아래 build.gradle 처럼 springdoc-openapi-starter-webmvc-ui 를 추가하면 된다.

  • implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'

springboot v3 을 사용한다면 springdoc-openapi 2.x 를 사용해야 한다. springdoc-openapi-starter-webmvc-uispringdoc-openapi-ui의 openapi-v2 이다.

// build.gradle
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.1'
    id 'io.spring.dependency-management' version '1.1.4'
}

group = 'com.nsm'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '17'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
}

tasks.named('test') {
    useJUnitPlatform()
}

test

내 springBoot server가 localhost:8080 에 떠있는 경우, 다음 경로로 가면 api 문서 화면이 보인다.

json format은

disable

다음 옵션 2개를 꺼주면 된다.

springdoc.api-docs.enabled=false
springdoc.swagger-ui.enabled=false

문서작성 annotation

일부는 자동으로 만들어주지만, 정확하지 않은 값으로 만들어준다. 명시적으로 api 문서에 값을 넣을때는 annotation 을 활용할 수 있다.

아래를 참고하면 된다.

다만, @RequestBody의 경우 spring 의 annotation 과 겹쳐서 @io.swagger.v3.oas.annotations.parameters.RequestBody 등으로 사용해야 할 수 있다.

@PostMapping
@Operation(summary = "Create a User", description = "This can only be done by the logged in user.")
ResponseEntity<MyResponse> createUser(
        @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Created User object", 
          required = true, content = @Content(schema = @Schema(implementation = MyPostBody.class))) 
        @Valid @RequestBody MyPostBody user) {
    return ResponseEntity.status(HttpStatus.CREATED)
            .body(userService.createOne(user));
}

Reference

  1. OpenAPI 3 Library for spring-boot

[컴] 구글 워크스페이스 특정계정의 2단계인증 중지 방법

구글 계정/ 구글 어드민 / google workspace / google gsuite /

구글 워크스페이스 특정계정의 2단계인증 중지 방법

2024-01-01 화면 기준

  1. https://admin.google.com/
  2. 메뉴 –> 디렉토리 –> 사용자
  3. 수정하기를 원하는 사용자의 이름을 클릭 –> 상세화면이 보인다.
  4. ‘보안’ 부분으로 간다. 여기서 확장(expand) icon 클릭
  5. ‘본인 확인 요청’ 클릭
  6. 사용 중지

References

  1. 본인 확인 요청, 2단계 인증, 로그인 관련 문제 해결하기 - Google Workspace 관리자 고객센터
  2. 사용자의 본인 확인 요청 일시적으로 사용 중지하기 - Google Workspace 관리자 고객센터

[컴] 구글 클라우드에 만들어 놓은 project 의 owner 계정이 사라지면 project 는 같이 지워질까?

구글 클라우드에 만들어 놓은 project 의 owner 계정이 사라지면 project 는 같이 지워질까?

google cloud(https://console.cloud.google.com/) 에 만들어 놓은 project 의 owner 계정이 사라지면 project 는 같이 지워질까?

개인 계정인 경우는 그렇다.

그러나 google workspace 같은 경우라면 다르다.

  • 계정(account) 가 organization(조직) 에 속해있으면, project 는 organization 에 속하게 됨.
  • 그래서 organization 의 admin 이 접속할 수 있게 됨.
  • IAM page 가서 새로운 owner 를 할당할 수 있다.

Reference

  1. What happens when we delete the google account of a project owner? - Stack Overflow