728x90

그런데? 인덱스를 밀어넣고 난 이후에도 데이터가 제대로 보이지 않는다는 것이었다.

흠. 이 부분에 대해서 킹치만 많은 가설을 통해 검증을 시작하면

 

========================================
ubuntu@ip-172-31-40-155:~$ curl -X GET "http://localhost:9200/_cat/indices?v"
health status index                                        uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   moyamo_3                                     9bPawqGQTseRptxkTTsy-A   5   1    1576206          774      1.3gb          1.3gb
yellow open   net.infobank.moyamo.models.photorecentwriter bYxm8ozGSzOmC8LpV59oWg   5   1          0            0       810b           810b

내가 예상한 데이터와 다르게 얼마 안들어갔다는 것이다?

 

 

항목 수치
기존 인덱싱 1,576,206
누락된 문서 14,759,000

 

이 부분을 계산해볼때 뭔가 이상한 느낌이 스물스물 나서 코드를 조사해봤는데

// SimdaSchedulerApplication.java:122
List<SimplePosting> postings = eventWorkRepositoryCustom.findPostingList(
    Posting.class, sinceId, maxId, 1000  // ← 최대 1000개만 조회!
);

1000개씩만 드신다는 것이었다.

 

그래서 이걸 진짜로 9~10시간에 걸려서 삽질을 해야되냐? 댓츠논노

우리는 무적의 m5.xlarge를 사용하고 있기 때문에 더욱 공격적으로 병렬로 때려넣을 수 있다는 것이다.

 

#!/bin/bash
# Elasticsearch 병렬 인덱싱 스크립트
# 8개의 프로세스로 병렬 처리 - m5.xlarge (4 vCPU, 16GB) 최적화
# 예상 소요 시간: 약 1시간

SCHEDULER_URL="http://localhost:8088"
MAX_COMMENTS=200

# ID 범위를 n등분해서 처리
reindex_range() {
    local START=$1
    local END=$2
    local WORKER=$3
    local BATCH_SIZE=1000
    local CURRENT=$START
    local COUNT=0

    echo "[Worker $WORKER] 시작: $START ~ $END"

    while [ $CURRENT -le $END ]; do
        local BATCH_END=$((CURRENT + BATCH_SIZE - 1))
        if [ $BATCH_END -gt $END ]; then
            BATCH_END=$END
        fi

        curl -s -X POST "$SCHEDULER_URL/indexing?sinceId=$CURRENT&maxId=$BATCH_END&max=$MAX_COMMENTS" --max-time 300 > /dev/null

        CURRENT=$((BATCH_END + 1))
        COUNT=$((COUNT + 1))

        # 500 배치마다 진행상황 출력
        if [ $((COUNT % 500)) -eq 0 ]; then
            echo "[Worker $WORKER] 진행 중... $CURRENT / $END"
        fi
    done

    echo "[Worker $WORKER] 완료!"
}

echo "========================================"
echo "병렬 인덱싱 시작 (8 workers)"
echo "서버: m5.xlarge (4 vCPU, 16GB RAM)"
echo "========================================"

START_TIME=$(date +%s)

# 8개의 범위로 나눠서 병렬 실행 (각 약 2백만 건)
reindex_range 114 2000000 1 &
reindex_range 2000001 4000000 2 &
reindex_range 4000001 6000000 3 &
reindex_range 6000001 8000000 4 &
reindex_range 8000001 10000000 5 &
reindex_range 10000001 12000000 6 &
reindex_range 12000001 14000000 7 &
reindex_range 14000001 16335566 8 &

# 모든 백그라운드 작업 완료 대기
wait

END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
HOURS=$((DURATION / 3600))
MINUTES=$(( (DURATION % 3600) / 60 ))
SECS=$((DURATION % 60))

echo "========================================"
echo "전체 인덱싱 완료!"
echo "소요 시간: ${HOURS}시간 ${MINUTES}분 ${SECS}초"
echo "========================================"

# 최종 문서 수 확인
echo "인덱싱된 문서 수:"
curl -s "http://localhost:9200/moyamo_3/_count"
echo """

 

 

하지만 이렇게 처리한 이후에 CPU는 영 좋지못한 상황으로 가버리는데

ubuntu@ip-172-31-40-155:~$ chmod +x reindex-parallel.sh 
ubuntu@ip-172-31-40-155:~$ ./reindex-parallel.sh 
========================================
병렬 인덱싱 시작 (8 workers)
서버: m5.xlarge (4 vCPU, 16GB RAM)
========================================
[Worker 1] 시작: 114 ~ 2000000
[Worker 2] 시작: 2000001 ~ 4000000
[Worker 7] 시작: 12000001 ~ 14000000
[Worker 6] 시작: 10000001 ~ 12000000
[Worker 4] 시작: 6000001 ~ 8000000
[Worker 3] 시작: 4000001 ~ 6000000
[Worker 5] 시작: 8000001 ~ 10000000
[Worker 8] 시작: 14000001 ~ 16335566

 

 

괘..괜찮아.. 새벽에 돌리는 Scheduler니까 서비스는 영향받지 않는다굿!

728x90

문제 상황

대략적으로 서술해보자면 서비스는 일단 502 Bad Gateway가 나면서 내려가고 식은땀이 줄줄 흐르는 상황이 발생한 것인데, 에러 로그만 수집해보면 다음과 같다.

 

Information Gathering

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
[Rest][2026-03-30 03:49:03][ERROR][SpringApplication.java][reportFailure(860)] : Application run failed
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:162)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:577)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:771)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:763)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:438)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:339)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1329)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1318)
        at net.infobank.moyamo.MoyamoApplication.main(MoyamoApplication.java:16)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
        at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:142)
        at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104)
        at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:450)
        at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:199)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:181)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:159)
        ... 17 common frames omitted

 

그리고 2번째 놈의 로그

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'tokenAuthorizationFilter' defined in URL [jar:file:/home/ubuntu/moyamo-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/net/infobank/moyamo/configuration/TokenAuthorizationFilter.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'authorizationService' defined in URL [jar:file:/home/ubuntu/moyamo-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/net/infobank/moyamo/api/service/AuthorizationService.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userSecurityRepository' defined in net.infobank.moyamo.repository.UserSecurityRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Cannot create inner bean '(inner bean)#59221b97' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#59221b97': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.search.exception.SearchException: HSEARCH400035: Could not update mappings in index 'moyamo_3'
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:212)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:175)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:170)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAdaptableBeans(ServletContextInitializerBeans.java:155)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:87)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:259)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:233)
        at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:53)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5161)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
        at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
        at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
        at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:829)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
        at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
        at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
        at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.StandardService.startInternal(StandardService.java:433)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:930)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.startup.Tomcat.start(Tomcat.java:486)
        at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:123)
        ... 22 common frames omitted

 

 

3번째 로그와 4번째 로그

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'authorizationService' defined in URL [jar:file:/home/ubuntu/moyamo-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/net/infobank/moyamo/api/service/AuthorizationService.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userSecurityRepository' defined in net.infobank.moyamo.repository.UserSecurityRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Cannot create inner bean '(inner bean)#59221b97' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#59221b97': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.search.exception.SearchException: HSEARCH400035: Could not update mappings in index 'moyamo_3'
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
        ... 63 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userSecurityRepository' defined in net.infobank.moyamo.repository.UserSecurityRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Cannot create inner bean '(inner bean)#59221b97' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#59221b97': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.search.exception.SearchException: HSEARCH400035: Could not update mappings in index 'moyamo_3'
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:389)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:134)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1689)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1434)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
        ... 77 common frames omitted

 

로그로그

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#59221b97': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.search.exception.SearchException: HSEARCH400035: Could not update mappings in index 'moyamo_3'
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:693)
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:510)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1334)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:374)
        ... 91 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.search.exception.SearchException: HSEARCH400035: Could not update mappings in index 'moyamo_3'
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1786)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
        ... 99 common frames omitted
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.search.exception.SearchException: HSEARCH400035: Could not update mappings in index 'moyamo_3'
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:421)
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396)
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1845)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782)
        ... 106 common frames omitted
Caused by: org.hibernate.search.exception.SearchException: HSEARCH400035: Could not update mappings in index 'moyamo_3'
        at org.hibernate.search.elasticsearch.schema.impl.DefaultElasticsearchSchemaMigrator.migrate(DefaultElasticsearchSchemaMigrator.java:77)
        at org.hibernate.search.elasticsearch.impl.ElasticsearchIndexManager.initializeIndex(ElasticsearchIndexManager.java:321)
        at org.hibernate.search.elasticsearch.impl.ElasticsearchIndexManager.initializeIndex(ElasticsearchIndexManager.java:266)
        at org.hibernate.search.elasticsearch.impl.ElasticsearchIndexManager.setSearchFactory(ElasticsearchIndexManager.java:257)
        at org.hibernate.search.indexes.impl.IndexManagerHolder.setActiveSearchIntegrator(IndexManagerHolder.java:150)
        at org.hibernate.search.engine.impl.MutableSearchFactoryState.setActiveSearchIntegrator(MutableSearchFactoryState.java:244)
        at org.hibernate.search.spi.SearchIntegratorBuilder.buildNewSearchFactory(SearchIntegratorBuilder.java:214)
        at org.hibernate.search.spi.SearchIntegratorBuilder.buildSearchIntegrator(SearchIntegratorBuilder.java:128)
        at org.hibernate.search.hcore.impl.HibernateSearchSessionFactoryObserver.boot(HibernateSearchSessionFactoryObserver.java:127)
        at org.hibernate.search.hcore.impl.HibernateSearchSessionFactoryObserver.sessionFactoryCreated(HibernateSearchSessionFactoryObserver.java:94)
        at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35)
        at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:385)
        at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:468)
        at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1259)
        at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
        ... 110 common frames omitted
        Suppressed: org.hibernate.search.exception.SearchException: HSEARCH400035: Could not update mappings in index 'net.infobank.moyamo.models.photorecentwriter'
                ... 127 common frames omitted
        Caused by: org.hibernate.search.exception.SearchException: HSEARCH400020: Could not create mapping for entity type net.infobank.moyamo.models.PhotoRecentWriter
                at org.hibernate.search.elasticsearch.schema.impl.ElasticsearchSchemaAccessor.putMapping(ElasticsearchSchemaAccessor.java:111)
                at org.hibernate.search.elasticsearch.schema.impl.DefaultElasticsearchSchemaMigrator.migrate(DefaultElasticsearchSchemaMigrator.java:73)
                ... 126 common frames omitted
        Caused by: org.hibernate.search.exception.SearchException: HSEARCH400007: Elasticsearch request failed.
Request: PUT /net.infobank.moyamo.models.photorecentwriter/net.infobank.moyamo.models.PhotoRecentWriter/_mapping with parameters {}
Response: 400 'Bad Request' with body
{
  "error": {
    "root_cause": [
      {
        "type": "illegal_argument_exception",
        "reason": "Mapper for [id] conflicts with existing mapping in other types:\n[mapper [id] has different [store] values]"
      }
    ],
    "type": "illegal_argument_exception",
    "reason": "Mapper for [id] conflicts with existing mapping in other types:\n[mapper [id] has different [store] values]"
  },
  "status": 400
}
                at org.hibernate.search.elasticsearch.work.impl.SimpleElasticsearchWork.handleResult(SimpleElasticsearchWork.java:101)
                at org.hibernate.search.elasticsearch.work.impl.SimpleElasticsearchWork.lambda$execute$3(SimpleElasticsearchWork.java:63)
                at java.util.concurrent.CompletableFuture.uniCompose(CompletableFuture.java:966)
                at java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:940)
                at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:488)
                at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:1975)
                at org.hibernate.search.elasticsearch.client.impl.DefaultElasticsearchClient$1.onFailure(DefaultElasticsearchClient.java:120)
                at org.elasticsearch.client.RestClient$FailureTrackingResponseListener.onDefinitiveFailure(RestClient.java:857)
                at org.elasticsearch.client.RestClient$1.completed(RestClient.java:560)
                at org.elasticsearch.client.RestClient$1.completed(RestClient.java:537)
                at org.apache.http.concurrent.BasicFuture.completed(BasicFuture.java:122)
                at org.apache.http.impl.nio.client.DefaultClientExchangeHandlerImpl.responseCompleted(DefaultClientExchangeHandlerImpl.java:181)
                at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.processResponse(HttpAsyncRequestExecutor.java:448)
                at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.inputReady(HttpAsyncRequestExecutor.java:338)
                at org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(DefaultNHttpClientConnection.java:265)
                at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:81)
                at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:39)
                at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:114)
                at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162)
                at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337)
                at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)
                at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276)
                at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
                at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)
                at java.lang.Thread.run(Thread.java:750)
        Caused by: org.hibernate.search.exception.SearchException: HSEARCH400090: Elasticsearch response indicates a failure.
                at org.hibernate.search.elasticsearch.work.impl.DefaultElasticsearchRequestSuccessAssessor.checkSuccess(DefaultElasticsearchRequestSuccessAssessor.java:107)
                at org.hibernate.search.elasticsearch.work.impl.DefaultElasticsearchRequestSuccessAssessor.checkSuccess(DefaultElasticsearchRequestSuccessAssessor.java:90)
                at org.hibernate.search.elasticsearch.work.impl.SimpleElasticsearchWork.handleResult(SimpleElasticsearchWork.java:92)
                ... 24 common frames omitted
Caused by: org.hibernate.search.exception.SearchException: HSEARCH400020: Could not create mapping for entity type net.infobank.moyamo.models.AdoptComment
        at org.hibernate.search.elasticsearch.schema.impl.ElasticsearchSchemaAccessor.putMapping(ElasticsearchSchemaAccessor.java:111)
        at org.hibernate.search.elasticsearch.schema.impl.DefaultElasticsearchSchemaMigrator.migrate(DefaultElasticsearchSchemaMigrator.java:73)
        ... 126 common frames omitted
Caused by: org.hibernate.search.exception.SearchException: HSEARCH400007: Elasticsearch request failed.
Request: PUT /moyamo_3/net.infobank.moyamo.models.AdoptComment/_mapping with parameters {}
Response: 400 'Bad Request' with body
{
  "error": {
    "root_cause": [
      {
        "type": "illegal_argument_exception",
        "reason": "mapper [relation.posting.title] of different type, current_type [text], merged_type [keyword]"
      }
    ],
    "type": "illegal_argument_exception",
    "reason": "mapper [relation.posting.title] of different type, current_type [text], merged_type [keyword]"
  },
  "status": 400
}


        at org.hibernate.search.elasticsearch.work.impl.SimpleElasticsearchWork.handleResult(SimpleElasticsearchWork.java:101)
        at org.hibernate.search.elasticsearch.work.impl.SimpleElasticsearchWork.lambda$execute$3(SimpleElasticsearchWork.java:63)
        at java.util.concurrent.CompletableFuture.uniCompose(CompletableFuture.java:966)
        at java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:940)
        at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:488)
        at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:1975)
        at org.hibernate.search.elasticsearch.client.impl.DefaultElasticsearchClient$1.onFailure(DefaultElasticsearchClient.java:120)
        at org.elasticsearch.client.RestClient$FailureTrackingResponseListener.onDefinitiveFailure(RestClient.java:857)
        at org.elasticsearch.client.RestClient$1.completed(RestClient.java:560)
        at org.elasticsearch.client.RestClient$1.completed(RestClient.java:537)
        at org.apache.http.concurrent.BasicFuture.completed(BasicFuture.java:122)
        at org.apache.http.impl.nio.client.DefaultClientExchangeHandlerImpl.responseCompleted(DefaultClientExchangeHandlerImpl.java:181)
        at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.processResponse(HttpAsyncRequestExecutor.java:448)
        at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.inputReady(HttpAsyncRequestExecutor.java:338)
        at org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(DefaultNHttpClientConnection.java:265)
        at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:81)
        at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:39)
        at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:114)
        at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162)
        at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337)
        at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)
        at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276)
        at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
        at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)
        at java.lang.Thread.run(Thread.java:750)
Caused by: org.hibernate.search.exception.SearchException: HSEARCH400090: Elasticsearch response indicates a failure.
        at org.hibernate.search.elasticsearch.work.impl.DefaultElasticsearchRequestSuccessAssessor.checkSuccess(DefaultElasticsearchRequestSuccessAssessor.java:107)
        at org.hibernate.search.elasticsearch.work.impl.DefaultElasticsearchRequestSuccessAssessor.checkSuccess(DefaultElasticsearchRequestSuccessAssessor.java:90)
        at org.hibernate.search.elasticsearch.work.impl.SimpleElasticsearchWork.handleResult(SimpleElasticsearchWork.java:92)
        ... 24 common frames omitted

 

 

 

대충 상황요약

HSEARCH400035: Could not update mappings in index 'moyamo_3'
Mapper for [id] conflicts with existing mapping in other types:
[mapper [id] has different [store] values]

 

ElasticSearch + Hibernate Search 관련 매핑 충돌 에러였다.

이게 왜 문제가 발생했냐면 PhotoRecentWriter 엔티티 id 필드 매핑이 기존 ElasticSearch 인덱스 매핑이랑 충돌한 것인데.

쥔짜 식은땀을 흘렷는데 예전 개발자녀석 왜 이렇게 짜놧는지 이해가 안감. 대충 아래의 소스를 살펴보면

 

package net.infobank.moyamo.models;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.NumericField;

import javax.persistence.*;
import java.time.ZonedDateTime;

@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Indexed
public class PhotoRecentWriter {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @NumericField
    private Long id;

    @NonNull
    @Embedded
    private UserPostingRelation relation;

    private ZonedDateTime createdAt;

    public PhotoRecentWriter(@NonNull UserPostingRelation userPostingRelation, ZonedDateTime createdAt){
        this.relation = userPostingRelation;
        this.createdAt = createdAt;
    }
}

 

이 부분에서 저기 보이는 Indexed 어노테이션을 사용하면서 생기는 문제였다. 인덱스 이름을 지정하지 않아서 생기는 문제인 것인데, 그래서 기본 클래스명인 net.infobank.moyamo.models.photorecentwriter 으로 별도 인덱스가 만들어졌던거임.

 

근데? 에러 로그를 보면 다음과 같다.

Could not update mappings in index 'moyamo_3'

moyamo_3 인덱스 내에서 PhotoRecentWriter의 id 필드가 다른 엔티티 의 id 필드 매핑이랑 store 값이 충돌하기 때문

 

대충 차이점을 잡고가자면 이런 부분이다

 

  • User.java, Posting.java 의 id:@NumericFiled + @SortableField (store 속성 없음 = 기본값 Store.NO)
  • PhotoRecentWriter.java의 id:@NumericField만 있다는 것

이 부분을 해결하려는 방법은 되게 간단했다.

PhotoRecentWriter의 id 필드에 동일한 어노테이션을 적용하거나, 아예 ElasticSearch 인덱싱이 필요 없으면 Indexed 어노테이션을 제거하면 된다.

@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class PhotoRecentWriter {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NonNull
    @Embedded
    private UserPostingRelation relation;

    private ZonedDateTime createdAt;

    public PhotoRecentWriter(@NonNull UserPostingRelation userPostingRelation, ZonedDateTime createdAt){
        this.relation = userPostingRelation;
        this.createdAt = createdAt;
    }
}

실제로 PhotoRecentWriter 가 검색기능에 사용되지 않으므로 제거하면되는 부분이므로 JPA엔티니니까 DB에서만 쓰는거임 ㅇㅇ

 

 

그런데? 난 사실 이걸 먼저하기보다 이 짓을 먼저했음

curl -X DELETE "localhost:9200/net.infobank.moyamo.models.photorecentwriter"

그러니까 502 Bad Gateway는 해결되서 올라오긴 하는데, 사람들이 예전에 쓴 글이 안올라온다고 하는 대략난감의 사태가 벌어졌다는 거쉼..

 

이게 영향이 있냐 없냐 고민을 하다보니 100% 있다는 것임!

엔드포인트 기능
GET /v2/users/me/postings 내가 쓴 글 목록
GET /v2/users/me/comments 내가 댓글 단 글 목록
GET /v2/users/{id}/postings 특정 사용자가 쓴 글 목록
GET /v2/users/{id}/comments 특정 사용자가 댓글 단 목록

 

 

그렇게 시작되었다. Hibernate Search에서 기존 DB 데이터를 ElasticSearch에 다시 인덱싱하는 삽질의 작업

식은땀을 줄줄 흘리다가. 나자신 생각했다. 하늘이 무너져도 솟아날 구멍? 댓츠논노

 

어쨋든 저놈들은 죄다 ElasticSearch 기반의 작업일꺼니까 인덱스만 다시 치면 된다는 생각이었음.

솔직히 JPA 안에 본체가 있는데 ElasticSearch가 캐싱하는 데이터 구도라면? 캐싱만 치면 되는 부분임

 

심지어 찾아보니 땅굴을 좀 파놧는데 이런 것들이 있었음

 

1. Posting 인덱스 재구축

# POST 요청 - sinceId부터 maxId까지 게시글 인덱싱
curl -X POST "http://<scheduler-server>/indexing?sinceId=0&maxId=999999999&max=200"

 

또는 api-server 에서

# 특정 게시글 ID들 인덱싱
curl -X POST "http://<api-server>/v2/postings/index?ids=1,2,3,4,5&max=200"

 

2. User 인덱스 재구축

# moyamo-api-server에서
curl -X GET "http://<api-server>/v2/users/index"

 

 

그런데 아무리 생각해봐도 API에다가 저짓을 하다가는 서버가 죽을 것이 뻔했음.

MySQL 에서 데이터를 조사함

-- MySQL에서 게시글 ID 범위 확인
SELECT MIN(id), MAX(id) FROM posting WHERE is_delete = false;

 

 

참으로 기모찌한 결과가 나온 것인데, 이걸 curl 로 나눠서 쏘다가는 꽤 귀찮을 것 같다는 거쉬..

 

그래서 인덱스 스크립트를 만들어서 때려박기로 했다.

#!/bin/bash
# Elasticsearch 인덱스 재구축 스크립트 (Linux/Mac용)
# 게시글 ID 범위: 114 ~ 16,335,566

SCHEDULER_URL="http://localhost:8088"  # moyamo-scheduler 포트: 8088
BATCH_SIZE=10000                        # 한 번에 처리할 ID 범위
MAX_COMMENTS=200                        # 댓글 수 제한
DELAY_SECONDS=2                         # 배치 간 대기 시간

MIN_ID=114
MAX_ID=16335566

CURRENT_ID=$MIN_ID
TOTAL_BATCHES=$(( (MAX_ID - MIN_ID + BATCH_SIZE - 1) / BATCH_SIZE ))
CURRENT_BATCH=0

echo "========================================"
echo "Elasticsearch 인덱스 재구축 시작"
echo "총 배치 수: $TOTAL_BATCHES"
echo "ID 범위: $MIN_ID ~ $MAX_ID"
echo "배치 크기: $BATCH_SIZE"
echo "========================================"

START_TIME=$(date +%s)

while [ $CURRENT_ID -le $MAX_ID ]; do
    CURRENT_BATCH=$((CURRENT_BATCH + 1))
    END_ID=$((CURRENT_ID + BATCH_SIZE - 1))
    if [ $END_ID -gt $MAX_ID ]; then
        END_ID=$MAX_ID
    fi

    PROGRESS=$(echo "scale=1; $CURRENT_BATCH * 100 / $TOTAL_BATCHES" | bc)
    echo "[$CURRENT_BATCH/$TOTAL_BATCHES] ($PROGRESS%) 인덱싱 중: $CURRENT_ID ~ $END_ID"

    RESPONSE=$(curl -s -X POST "$SCHEDULER_URL/indexing?sinceId=$CURRENT_ID&maxId=$END_ID&max=$MAX_COMMENTS" --max-time 300)

    if [ $? -eq 0 ]; then
        echo "  완료: $RESPONSE"
    else
        echo "  오류 발생, 재시도 중..."
        sleep 5
        RESPONSE=$(curl -s -X POST "$SCHEDULER_URL/indexing?sinceId=$CURRENT_ID&maxId=$END_ID&max=$MAX_COMMENTS" --max-time 300)
        if [ $? -eq 0 ]; then
            echo "  재시도 완료: $RESPONSE"
        else
            echo "  재시도 실패, 다음 배치로 진행"
        fi
    fi

    CURRENT_ID=$((END_ID + 1))

    if [ $CURRENT_ID -le $MAX_ID ]; then
        sleep $DELAY_SECONDS
    fi
done

END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
HOURS=$((DURATION / 3600))
MINUTES=$(( (DURATION % 3600) / 60 ))
SECONDS=$((DURATION % 60))

echo "========================================"
echo "인덱싱 완료!"
echo "소요 시간: ${HOURS}시간 ${MINUTES}분 ${SECONDS}초"
echo "========================================"

일단 테스트도 완료했겠다 신나게 돌려본다는 것인데..

 

그 전에 주머니에서 주섬주섬 moyamo-common 부터 말은 이후에 api-server를 말아서 build.jar로 만든다음에 배포 했다는 스토리는 생략하도록 하고, 저게 돌아가는건 결국 스케쥴러가 돌아가는 인스턴스에서 돌아가고 있는 부분이었으니까 포트부터 조사하면

스크립트 → moyamo-scheduler → Elasticsearch
              (/indexing API)      (내부적으로 연결)

 

이런 구조가 나온다는 것이었다.

 

 

생각해보니 Tools 서버를 껏다키면서 ElasticSearch 때문에 잘 안올라오는 다는 부분을 생각해보면서 볼륨 detach 했다가 다시 attach 하고 그런 부분 와중에 데몬을 제대로 안살렸었더라는 기억이 있었으므로 스케쥴러부터 살려줌

ubuntu@ip-172-31-40-155:~/workspace/scheduler/logs$ ps -ef | grep scheduler
ubuntu   15665  6442  0 12:34 pts/1    00:00:00 grep --color=auto scheduler
ubuntu@ip-172-31-40-155:~/workspace/scheduler/logs$ cd ..
ubuntu@ip-172-31-40-155:~/workspace/scheduler$ ls -alk
total 40
drwxrwxr-x 5 ubuntu ubuntu  4096 Oct  7  2024 .
drwxrwxr-x 4 ubuntu ubuntu  4096 Mar 30  2021 ..
drwxrwxr-x 2 ubuntu ubuntu  4096 Oct 21  2024 bin
drwxrwxr-x 2 ubuntu ubuntu 16384 Mar 30 12:32 logs
-rwxrwxr-x 1 ubuntu ubuntu   253 Oct 13  2023 start.sh
drwxrwxr-x 3 ubuntu ubuntu  4096 Sep  7  2020 static
-rwxrwxr-x 1 ubuntu ubuntu    58 Apr 16  2021 stop.sh
ubuntu@ip-172-31-40-155:~/workspace/scheduler$ vi start.sh 
ubuntu@ip-172-31-40-155:~/workspace/scheduler$ vi start.sh 
ubuntu@ip-172-31-40-155:~/workspace/scheduler$ ./start.sh 
ubuntu@ip-172-31-40-155:~/workspace/scheduler$ ps -ef | grep scheduler
ubuntu   18233     1 99 12:36 pts/1    00:00:11 java -Xmx1500m -Xms1500m -jar -Dspring.profiles.active=product -Dmoyamo.jobs.rankings.cron=0 */1 * * * * -Dmoyamo.jobs.statistics.cron=0 */1 * * * * /home/ubuntu/workspace/scheduler/bin/moyamo-scheduler-0.0.1-SNAPSHOT.jar
ubuntu   18373  6442  0 12:36 pts/1    00:00:00 grep --color=auto scheduler

 

 

이제 언제 살아날지 모르는 대망의 작업을 시작한다.

ubuntu@ip-172-31-40-155:~$ chmod +x reindex-elasticsearch.sh 
ubuntu@ip-172-31-40-155:~$ ./reindex-elasticsearch.sh 
========================================
Elasticsearch 인덱스 재구축 시작
총 배치 수: 1634
ID 범위: 114 ~ 16335566
배치 크기: 10000
========================================
[1/1634] (0%) 인덱싱 중: 114 ~ 10113
  완료: end
[2/1634] (.1%) 인덱싱 중: 10114 ~ 20113
  완료: end
[3/1634] (.1%) 인덱싱 중: 20114 ~ 30113
  완료: end
[4/1634] (.2%) 인덱싱 중: 30114 ~ 40113
  완료: end
[5/1634] (.3%) 인덱싱 중: 40114 ~ 50113
  완료: end
[6/1634] (.3%) 인덱싱 중: 50114 ~ 60113
  완료: end
[7/1634] (.4%) 인덱싱 중: 60114 ~ 70113
  완료: end
[8/1634] (.4%) 인덱싱 중: 70114 ~ 80113
  완료: end
[9/1634] (.5%) 인덱싱 중: 80114 ~ 90113
  완료: end
[10/1634] (.6%) 인덱싱 중: 90114 ~ 100113
  완료: end
[11/1634] (.6%) 인덱싱 중: 100114 ~ 110113
  완료: end
[12/1634] (.7%) 인덱싱 중: 110114 ~ 120113
  완료: end
[13/1634] (.7%) 인덱싱 중: 120114 ~ 130113
  완료: end
[14/1634] (.8%) 인덱싱 중: 130114 ~ 140113
  완료: end
[15/1634] (.9%) 인덱싱 중: 140114 ~ 150113
  완료: end
[16/1634] (.9%) 인덱싱 중: 150114 ~ 160113

 

 

728x90

 

대충 서론적으로 표현하자면 EMMA 는 인포뱅크에서 만든 문자메세지 발송 서비스이다. AWS aurora RDS 8.0 으로 버전업그레이드를 하게 되면서 여러가지 불편한 사항이 많았는데, 특히 이번 문제는 추가적으로 전체적인 시스템을 간과하지 못해서 일어났던 실수이다.

 

대충 moyamo 의 내부 instance를 모듈별로 나눠보자면 아래와 같은데

 

moyamo-rest (주로 API 에 대한 부분을 전반적으로 담당하고 있다.)

moyamo-tools (ElasticSearch 라던지, EMMA (Client), EMMA-Server, Scheduler 를 통합 담당한다.)

- 특히 이 친구의 경우 moyamo-schedule 인스턴스에서 모듈이 동시에 돌아가게 되는 경우에는 망할놈의 랭킹을 2배로 집계시킨다거나 하는 비정상적인 현상을 발현시킨다.

- 아직까지도 그 퇴사한 개발자들이 해당 인스턴스를 Deprecated 시키고 퇴역시키지 않고, History 조차 남기지 않아서 혼란을 주게 하는 이유는 아직도 파악할 수 없다.

 

뭐 등등의 인스턴스가 있지만 대충 이 정도로 포함해본다.


Information gathering

가장 의아했던것은, moyamo-tools 가 예전에 한번 말썽을 일으킨 적이 있기도 하고 Volume 확장의 문제가 있기도 했어서 재부팅이 한번 들어갔던 적이 있었다. (그 뒤로는 HA 구성을 잡았지만.)

 

그 뒤에도 이 안에 있던 EMMA 라는 문자 메세지 발송 서비스는 정상적으로 작동되고 있었는데, 언제인가부터 문자 메세지 발송 서비스가 정상적으로 작동하지 않았다.

 

아까 위에서도 써놨지만 이 EMMA 라는 놈의 구조는 총 2가지로 나뉘어져 있는데

EMMA-Server 라고 불리는 Server 와, 그냥 EMMA로 표시되어 있는 Client 로 나뉜다.

 

문제는 Server는 정상적으로 작동하고 있는데 (가끔 포트 부분에서 Duplicated 문제가 발생하긴 했지만) Client 에서 다음과 같은 에러를 뿜으면서 작동하지 않았다.

 

인스턴스 내의 syscheck 라는 shell 을 가동했을 시

ubuntu@ip-172-31-40-155:~/workspace/emma$ ./syscheck 
DB[jdbc:mysql://moyamo-product.cluster-c7mmfvbcxzve.ap-northeast-2.rds.amazonaws.com:3306/emma_db?useUnicode=true&noAccessToProcedureBodies=true] Check [NOK]
ib.frame.exception.DBException: Unknown initial character set index '255' received from server. Initial client character set can be forced via the 'characterEncoding' property.||Unknown initial character set index '255' received from server. Initial client character set can be forced via the 'characterEncoding' property.
        at ib.emma.db.GDBConnector.getConnection(GDBConnector.java:159)
        at ib.emma.main.DBCheck.connect(DBCheck.java:75)
        at ib.emma.main.SysCheck.main(SysCheck.java:75)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.exe4j.runtime.LauncherEngine.launch(Unknown Source)
        at com.install4j.runtime.Launcher.main(Unknown Source)
java.sql.SQLException: Unknown initial character set index '255' received from server. Initial client character set can be forced via the 'characterEncoding' property.
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:910)
        at com.mysql.jdbc.Connection.configureClientCharacterSet(Connection.java:2412)
        at com.mysql.jdbc.Connection.initializePropsFromServer(Connection.java:4139)
        at com.mysql.jdbc.Connection.createNewIO(Connection.java:2789)
        at com.mysql.jdbc.Connection.<init>(Connection.java:1555)
        at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:285)
        at java.sql.DriverManager.getConnection(DriverManager.java:664)
        at java.sql.DriverManager.getConnection(DriverManager.java:247)
        at ib.emma.db.GDBConnector.getConnection(GDBConnector.java:155)
        at ib.emma.main.DBCheck.connect(DBCheck.java:75)
        at ib.emma.main.SysCheck.main(SysCheck.java:75)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.exe4j.runtime.LauncherEngine.launch(Unknown Source)
        at com.install4j.runtime.Launcher.main(Unknown Source)
-----------------------------
log4j:WARN No appenders could be found for logger (ib.emma.db.GDBConnector).
log4j:WARN Please initialize the log4j system properly.

 

 

이 로그에서 저 오류는 

MySQL 클라이언트 드라이버와 서버 간의 문자셋 호환 문제에 기반한다. 구체적으로는, JDBC 드라이버가 서버에서 보낸 문자셋 인덱스 255를 인식하지 못해 생기는 오류다.

 

해결 방법으로는 아래와 같은 접근이 가능하다.

 

✅ 1. JDBC URL에 characterEncoding 추가

JDBC 연결 문자열에 characterEncoding=UTF-8 또는 useUnicode=true&characterEncoding=UTF-8을 명시적으로 추가

- 이미 해당하는 경우에는 EMMA 에 대해서 잘 아는 것은 아니지만 EMMA 가 불러다 사용하는 db.cnf 파일 안에 정의가 되어 있는 것을 확인할 수 있었다.

 

정확하게는 character Set은 EUC-KR 을 사용중이였다. 해당 부분을 바꾸면 안되는 이유는 아마도 추가적으로 통신하는 인포뱅크의 내부 시스템이 EUC-KR을 사용하고 있는 것을 생각해볼 수 있을 것 같다.


✅ 2. JDBC 드라이버 버전 확인 및 교체

이 문제는 구형 MySQL JDBC 드라이버에서 종종 발생한다. 드라이버를 최신 버전으로 교체해야 한다.

  • 예를 들어 mysql-connector-java-3.x 또는 5.1.x를 사용 중이라면, 8.0.x 이상으로 교체

- 이 부분에서 가장 크게 의심했던 것이 Aurora RDS 에 대한 버전 업그레이드 이슈와 동일 선상에 있다고 판단했다. 생각해보면 Aurora RDS의 버전 업그레이드 이후에 moyamo-product(REST) 쪽은 전부다 수정했지만, EMMA 쪽에 대해서는 내가 손댈 수 있는 것을 생각해본 적이 없기에 (정확히는 몰랐다.) 라이브러리 업데이트가 필요하다고 생각했다.

 

다만 추가적으로 생각했던 부분은 해당하는 EMMA 에 대해서 직접적으로 컴파일을 할 수 없는 상황이기에 lib 에 추가하고 클래스패스 내에서 갱신하는 방법을 고려했다.


✅ 3. MySQL 서버 설정 확인

MySQL 서버의 문자셋 설정이 너무 생소한 설정일 수 있을 가능성.

확인해 보니 다음과 같았다.

 

 

역시 나의 감대로 버전 이슈가 매우매우 눈에 들어오는 것이 아주 강한 냄새를 날려주고 있었다.

 

 


 

진격의 시작

################################################################################
#------------------------------------------------------------------------------# 
# Emma Database Configuration File                                             #
# EMMA Database ȯ°æ ¼³Á¤ ÆÄÀÏ                                                 #
# ÆÄ ÀÏ ¸í : db.cf                                                             #
#------------------------------------------------------------------------------#
# NOTE: ÀÌ È­ÀÏÀ» ¼öÁ¤ÇÏ°í ¹Ý¿µµÇ±â¸¦ ¿øÇϸé EMMA¸¦ »õ·Î ±âµ¿ÇØ¾ß ÇÑ´Ù.        #
#------------------------------------------------------------------------------#
################################################################################


#*******************************************************************************
# Database Connection Information
#*******************************************************************************

#-------------------------------------------------------------------------------
# ¿¬°áÇÒ DBMS °³¼ö
#-------------------------------------------------------------------------------
db.count = 1

#-------------------------------------------------------------------------------
# µ¥ÀÌÅͺ£À̽º ¿¬°áÀ» À§ÇÑ ¼³Á¤ 
# (db0.type : 1-Oracle, 2-MSSQL, 3-MySQL, 6-DB2, 7-Tibero, 8-Maria, 9-Sybase)
#-------------------------------------------------------------------------------
db0.type        = 3
db0.driver      = org.gjt.mm.mysql.Driver
#db0.url         = jdbc:mysql://172.27.218.169:3306/emma_db?useUnicode=true&noAccessToProcedureBodies=true
db0.url         = jdbc:mysql://moyamo-product.cluster-c7mmfvbcxzve.ap-northeast-2.rds.amazonaws.com:3306/emma_db?useUnicode=true&noAccessToProcedureBodies=true
db0.charset     = EUC-KR
db0.monum      =

 

이게 대충 해당하는 내용의 일부인데, 딱 보면 알겠지만 드라이버가 굉장히 구식 버전을 사용하고 있다. 해당 드라이버는 2002년에 단종된 드라이버인데, 이걸 아직까지 쓰고 있었다는게 좀 많이 놀라운 부분이였다.

 

해당하는 부분을 다음과 같은 드라이버 클래스로 변경해줬다.

db0.driver = com.mysql.cj.jdbc.Driver

 

 

dbcheck

이후에 dbcheck 라고 되어있는 shell 파일을 실행했다. (내용도 다 까본건 안비밀)

ubuntu@ip-172-31-40-155:~/workspace/emma$ ./dbcheck 
ib.frame.exception.DBException: com.mysql.cj.jdbc.Driver||com.mysql.cj.jdbc.Driver
        at ib.emma.db.GDBConnector.getConnection(GDBConnector.java:157)
        at ib.emma.main.DBCheck.connect(DBCheck.java:75)
        at ib.emma.main.DBCheck.main(DBCheck.java:267)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.exe4j.runtime.LauncherEngine.launch(Unknown Source)
        at com.install4j.runtime.Launcher.main(Unknown Source)
java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver
        at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:264)
        at ib.emma.db.GDBConnector.getConnection(GDBConnector.java:154)
        at ib.emma.main.DBCheck.connect(DBCheck.java:75)
        at ib.emma.main.DBCheck.main(DBCheck.java:267)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.exe4j.runtime.LauncherEngine.launch(Unknown Source)
        at com.install4j.runtime.Launcher.main(Unknown Source)
-----------------------------
log4j:WARN No appenders could be found for logger (ib.emma.db.GDBConnector).
log4j:WARN Please initialize the log4j system properly.

 

예상은 했지만 못찾는 문제가 발생했다. 당연히 구식 드라이버를 명시해주고 있었으니, 컴파일을 할 당시에 구식 드라이버를 포함시키는 컴파일을 해서 라이브러리 안쪽에 구식으로 올라가 있을 것이 분명했다.

 

 

drwxrwxr-x  2 ubuntu ubuntu    4096 Sep  2  2020 .
drwxrwxr-x 13 ubuntu ubuntu    4096 May 23 09:35 ..
-rw-r--r--  1 ubuntu ubuntu 1627539 Sep  2  2020 bcprov-jdk16-138.jar
-rw-r--r--  1 ubuntu ubuntu  121757 Sep  2  2020 commons-dbcp-1.2.2.jar
-rw-r--r--  1 ubuntu ubuntu  261809 Sep  2  2020 commons-lang-2.4.jar
-rw-r--r--  1 ubuntu ubuntu   87077 Sep  2  2020 commons-pool-1.4.jar
-rw-r--r--  1 ubuntu ubuntu  155858 Sep  2  2020 cubrid_jdbc.jar
-rw-r--r--  1 ubuntu ubuntu 2833724 Sep  2  2020 db2jcc.jar
-rw-r--r--  1 ubuntu ubuntu    1015 Sep  2  2020 db2jcc_license_cu.jar
-rw-r--r--  1 ubuntu ubuntu    7192 Sep  2  2020 dbguard_mob.jar
-rw-r--r--  1 ubuntu ubuntu  168644 Sep  2  2020 eb-standard-billing-1.2.15.jar
-rw-r--r--  1 ubuntu ubuntu  910690 Sep  2  2020 emma-3.4.12.jar
-rw-r--r--  1 ubuntu ubuntu   28067 Sep  2  2020 emma-bridge-alerter-1.0.0.jar
-rw-r--r--  1 ubuntu ubuntu   34147 Sep  2  2020 emma-bridge-custom-emma1-1.0.0.jar
-rw-r--r--  1 ubuntu ubuntu   62642 Sep  2  2020 emma-bridge-custom-emma3-1.0.0.jar
-rw-r--r--  1 ubuntu ubuntu     679 Sep  2  2020 emma-was-1.0.0.jar
-rw-r--r--  1 ubuntu ubuntu  481220 Sep  2  2020 google-collect-snapshot-20080820.jar
-rw-r--r--  1 ubuntu ubuntu  190418 Sep  2  2020 gson-2.2.4.jar
-rw-r--r--  1 ubuntu ubuntu  227991 Sep  2  2020 ibframe-1.1.4.jar
-rw-r--r--  1 ubuntu ubuntu  447867 Sep  2  2020 ibpdu-1.1.3.jar
-rw-r--r--  1 ubuntu ubuntu  301946 Sep  2  2020 jtds-1.2.8.jar
-rw-r--r--  1 ubuntu ubuntu  391834 Sep  2  2020 log4j-1.2.15.jar
-rw-r--r--  1 ubuntu ubuntu  287022 Sep  2  2020 msbase.jar
-rw-r--r--  1 ubuntu ubuntu   67115 Sep  2  2020 mssqlserver.jar
-rw-r--r--  1 ubuntu ubuntu   59074 Sep  2  2020 msutil.jar
-rw-r--r--  1 ubuntu ubuntu  540852 Sep  2  2020 mysql-connector-java-5.0.8-bin.jar
-rw-r--r--  1 ubuntu ubuntu 1555682 Sep  2  2020 ojdbc14.jar
-rw-r--r--  1 ubuntu ubuntu  448141 Sep  2  2020 postgresql-8.3-604.jdbc3.jar
-rw-r--r--  1 ubuntu ubuntu  475580 Sep  2  2020 postgresql-8.3-604.jdbc4.jar
-rw-r--r--  1 ubuntu ubuntu   22338 Sep  2  2020 slf4j-api-1.5.6.jar
-rw-r--r--  1 ubuntu ubuntu    9678 Sep  2  2020 slf4j-log4j12-1.5.6.jar
-rw-r--r--  1 ubuntu ubuntu  583286 Sep  2  2020 sqljdbc.jar
-rw-r--r--  1 ubuntu ubuntu  584207 Sep  2  2020 sqljdbc4.jar
-rw-r--r--  1 ubuntu ubuntu  725602 Sep  2  2020 tibero4-jdbc.jar
-rw-r--r--  1 ubuntu ubuntu  137211 Sep  2  2020 unisqljdbc3_0_7.jar

 

1주일 굶은 곰마냥 무언가 단서를 찾아서 찾아 해매다가 꿀단지를 발견했다. 그리고 다음과 같은 조치를 취해줬다.

 

 

일단 멀쩡하게 살고있던 녀석은 backup 본으로 밀어넣어주고

mv ~/workspace/emma/lib/mysql-connector-java-5.0.8-bin.jar ~/workspace/emma/lib/mysql-connector-java-5.0.8-bin.jar.bak

 

 

최신 드라이버를 낼름 가져와서 클래스패스 내에서 다시 연결시켜줬다.

cd ~/workspace/emma/lib

wget https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/8.3.0/mysql-connector-j-8.3.0.jar

ln -s mysql-connector-j-8.3.0.jar mysql-connector-java.jar

cd ~/workspace/emma
java -cp ".:./lib/*" ib.emma.main.DBCheck

 

UTF-8을 못알아 든는 친구가 존재했다.

ubuntu@ip-172-31-40-155:~/workspace/emma$ java -cp ".:./lib/*" ib.emma.main.DBCheck
log4j:WARN No appenders could be found for logger (ib.emma.db.GDBConnector).
log4j:WARN Please initialize the log4j system properly.
Console 한글 테스트
Driver Version: mysql-connector-j-8.3.0 (Revision: 805f872a57875f311cb82487efcfb070411a3fa0)
Server Version: 8.0.32
DB connection check is completed.
DB privileges check is completed.
query :  call sp_em_smt_tran_select(?, ?, ?, ?, ?); 
55987 / 01066814446 / [???] ???? 942677? ??? ???.
55988 / 01066814446 / [???] ???? 282570? ??? ???.
55989 / 01025617240 / [???] ???? 874947? ??? ???.
55990 / 01031871559 / [???] ???? 380585? ??? ???.
55991 / 01027772119 / [???] ???? 821225? ??? ???.
55992 / 01027772119 / [???] ???? 940578? ??? ???.
55993 / 01027772119 / [???] ???? 558572? ??? ???.
55994 / 01037710374 / [???] ???? 278919? ??? ???.
55995 / 01033570189 / [???] ???? 674359? ??? ???.
55996 / 01033570189 / [???] ???? 763137? ??? ???.
55997 / 01033570189 / [???] ???? 360590? ??? ???.
55998 / 01039008813 / [???] ???? 720691? ??? ???.
55999 / 01039008813 / [???] ???? 542344? ??? ???.
56000 / 01039008813 / [???] ???? 463278? ??? ???.
56001 / 01039008813 / [???] ???? 905421? ??? ???.
56002 / 01047992956 / [???] ???? 351778? ??? ???.
56003 / 01047992956 / [???] ???? 929949? ??? ???.
SMSMT check is completed.

 

일단 데이터베이스 체크는 신박하게 잘 돌아갔기 때문에, 돌아갈 것으로 예상하고 있었는데 db.cnf 파일 안에 EUC-KR 을 UTF-8 로 바꿔주었더니 문자를 받을 때에 한글이 ??? 로 오는 문제가 발생됬다.

 

따라서 EUC-KR로 재변경을 하는 부분이 있었다.

 


추가적으로 shell로 만들어진 파일이 2가지가 존재했는데

 

emma 와

emmasvc 라는 파일 이였다.

 

emma의 경우 데몬화시키지 않은 파일로써, 해당 쉘 파일을 실행시키면 컴파일된 프로그램이 프로세스가 되어 가동되고 화면에 그대로 보여지는 파일을 작성해 놓은 것이였다.

 

추가적으로 emmasvc 의 경우 start, stop, status 인 추가적인 options 를 포함하고 있는데, start를 시키면 nohup 이 걸리면서 데몬화가 되어지고 서비스로 가동되게 되어 있다.

- 다만 왜때문에 로그가 안남았는지는 모르겠으나 log4j에 대한 적절한 설정이 없었던 것 같은 느낌이 스물스물?

 

 

결론 = 문자메세지 잘 오게 정상적으로 잘 고쳤다.

'Java' 카테고리의 다른 글

Method: projects.messages.send  (1) 2024.10.01
FCM HTTP v1 Firewall Port bound  (4) 2024.09.30
MyBatis Mapper Setting  (0) 2024.09.24
Class Verify  (0) 2024.09.23
Java - Json 과 Gson 이란?  (0) 2024.09.23
728x90

문제 상황

400 Bad Request 가 나면서 지속적으로 망할놈의 Bad Request 만 발견되던 상황이였고, 이런 부분은 아래와 같이 해결할 수 있다. 

 

https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send

 

Method: projects.messages.send  |  Firebase Cloud Messaging REST API

 

firebase.google.com

 

이렇게 위의 해당하는 URL로 가게 되면 우측 창에 Payload 를 전개할 수 있는 창이 있는데, 해당하는 부분에 파라미터들을 추가하면서 실험해볼 수가 있다.

 


이걸 해결하기 전까지는 어떤 삽질들을 했을까?

1. Postman을 이용한 삽질

 

아래와 같이 Payload  를 정의한다.

 

POST 에 소스코드에서 추가했던 바처럼 message 의 send 를 보낼 수 있는 엔드포인트를 postman의 상단에 정의한다

POST https://fcm.googleapis.com/v1/projects/moyamo-plus/messages:send

 

 

Request Body 를 정의한다.

FCM HTTP v1 Payload 에서는 공통 부분을 정의할 때에는 (안드로이드와 iOS에 대해서 동시에 발송할 때에는) 반드시 message 하부에 notification 의 body가 정의되어야 한다. 

 

{
    "message":{
        "notification":{
            "title":"이름이 뭘까요?",
            "body":"알림테스트에 들어갑니다"
        },
        "token":"fG1sB-i_TRWsI1W9vjL0ql:APA91bFq8CvJ14HqaWDBDO7b4qNwk3avS4_RP849X-s801cIjXgRgfTHLULLpojpXI9tACW1hDmZEkINjXJpU6oh4y4YTIEm3k7LvYQO8Z0zOrkYEAIY1E3bbPtB77y3YhjALR0JF8XL",
        "android":{
            "notification":{
                "click_action":".MainActivity",
                "sound":"default"
            }
        }
    },
    "validate_only":false
}

 

 

android의 경우에는 notification 이 있고 title 과 body가 있을 경우 해당하는 속성을 override 해서 사용하므로 해당하는 부분만 참고하면 된다.

 

주의해야 할 부분에 대한 정의

1. Common Notification에 대한 Payload

FCM HTTP v1 Payload 에서는 공통 부분을 정의할 때에는 (안드로이드와 iOS에 대해서 동시에 발송할 때에는) 반드시 message 하부에 notification 의 body가 정의되어야 한다. 

 

 

2. Android Notification 과 Common Notification의 Conflict 

message의 common noficatication field 와 android 에서 해당하는 부분을 동시에 정의했을 경우 Push Notification 이 발송되었을 때, Android 내에서 Main Activity 로 가서 intent-filter 에 의해서 발동하는 clickAction 이 정상적으로 발동되지 않는다.

 

따라서 Main Activity 로 향하는 부분에 대한 정의만 필요하다면 굳이 Android 에 대한 부분을 정의하면서 최상단에 있는 Common Notification field 를 정의하진 말자

 

3. Custom Notication 에 대한 가능 여부

정확하게는 clickAction에 대한 유도라기 보다, Push Notification 에 대한 Custom Notification의 가능 여부는 당연히 가능하다.

 

모든 부분에 대한 사용 중에서 Data property 를 사용하면 되고, Payload 는 다음과 같이 전개하면 된다.

{
  "message": {
    "data": {
      "title": "<가을에 심는 구근> 알리움 2개 품종",
      "description": "test data close",
      "id": "15170295",
      "photoUrl": "",
      "referenceId": "15170295",
      "referenceType": "question",
      "resourceType": "question",
      "resourceId": "15170295"
    },
    "token": "fG1sB-i_TRWsI1W9vjL0ql:APA91bFq8CvJ14HqaWDBDO7b4qNwk3avS4_RP849X-s801cIjXgRgfTHLULLpojpXI9tACW1hDmZEkINjXJpU6oh4y4YTIEm3k7LvYQO8Z0zOrkYEAIY1E3bbPtB77y3YhjALR0JF8XL"
  },
  "validate_only": false
}

 

 

물론 이와같이 정의했을 경우 안드로이드에서 받아서 처리하는 Json에 대한 처리는 Android 내부에서 담당하게 되어있다.

+ Recent posts