Coder Social home page Coder Social logo

dev-diary's Introduction

Dev-Diary

실무 또는 개인 공부 과정에서 해결했던 문제나 개선점을 이슈로 정리하는 저장소

dev-diary's People

Contributors

saechimdaeki avatar

Stargazers

 avatar  avatar

Watchers

 avatar

dev-diary's Issues

Kotlin ObjectMapper사용시 런타임에 kotlin no creators, like default constructor, exist 로그가 출력되며 실행되지 않을경우

data class CheckOutDto(
    var checkOutId: Long?,
    val memberId: Long,
    val productId: Long,
    val amount : Long,
    val shippingAddress : String,
    var createdAt : LocalDate?
)

다음과 같은 dataClass가 있다고 했을 때에 JsonString으로 들어온 data를

val checkOutDto = objectMapper.readValue<CheckOutDto>(jsonMessage)

위와 같이 사용하려고 하면 아래와 같은 에러가 발생하게 된다.

(no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

해결 법은 간단하다 다음과 같이 ObjectMapper에 registerModule을 달아 주면된다.

ObjectMapper().registerModule(KotlinModule())

하지만 이는 deprecated가 되었다고 ide에서 경고를 준다. 현재 2023년 3월 기준 최신 방법인 다음과 같이 달아주면 된다.

image

return ObjectMapper().registerKotlinModule()

필자(saechimdaeki) 는 보통 경우 ObjectMapper를 다음과 같이 Bean으로 등록해서 사용함.

@Bean
fun customObjectMapper() : ObjectMapper {
        return ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .registerModule(JavaTimeModule())
            .registerKotlinModule()
}

Kotlin에서 QuerydslBinderCustomizer를 사용할 때 에러 해결 feat. -Xjvm-default option

@RepositoryRestResource
interface ArticleRepository : JpaRepository<Article, Long>,
    QuerydslPredicateExecutor<Article>,
    QuerydslBinderCustomizer<QArticle> {

    override fun customize(bindings: QuerydslBindings, root: QArticle) {
        bindings.excluding(root.id, root.comments, root.modifiedAt, root.modifiedBy)
        bindings.bind(root.content).first { path: StringExpression, value: String? -> path.containsIgnoreCase(value) }
        bindings.bind(root.hashtag).first { path: StringExpression, value: String? -> path.containsIgnoreCase(value) }
        bindings.bind(root.createdBy).first { path: StringExpression, value: String? -> path.containsIgnoreCase(value) }
        bindings.bind(root.createdAt).first(DateTimeExpression<LocalDateTime>::eq)
    }

}

querydsl에서 기본적으로 제공하는 기능들을 더 사용하려고 QuerydslBinderCustomizer를 사용하려고 하면 다음과 같은 에러가 발생하게 된다

No property 'customize' found for type 'Article'

흠 이에러가 무엇일까? kotlin에서만 발생하는 에러는 틀림없다. 알아본 결과

@JvmDefault
override fun customize(bindings: QuerydslBindings, root: QArticle) {
}

를 작성하면 된다고 한다.

하지만 컴파일러는 이를 빌드가 되지 않게 막는데 이유는 다음과 같다

Usage of '@JvmDefault' is only allowed with -Xjvm-default option

여기서 힌트를 얻을 수 있다.

바로 gradle에 저 옵션을 추가해주는 것이다.

기존 스프링부트 3 버젼이상의 kotlin프로젝트를 생성했을 때 기본은 다음과 같다

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "17"
    }
}

여기에 "-Xjvm-default=all" 옵션을 달아주면 @JvmDefault를 달지 않고도 컴파일이 되고 querydsl에서도 제공하는 기능인 QuerydslBinderCustomizer를 사용할 수 있다.

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict", "-Xjvm-default=all")
        jvmTarget = "17"
    }
}

해결..! 👋🏼

[Redis] redis DEL vs UNLINK

레디스를 사용하면서 배치에서 rdb가 아닌 레디스를 통해서 sortedSet의 데이터를 읽고 읽은것을 삭제시켜야 하는 프로젝트가 있다

현재 여기서 sortedSet의 데이터가 커서 del명령어를 수행하며 spring batch를 돌리고 있었으나 문제가 있었다

그렇게 자연스레 UNLINK라는 명령어를 알게되었고 둘의 차이는 다음과 같다

UNLINK와 DEL 명령은 모두 키와 그에 해당하는 값을 데이터베이스에서 제거하는 명령어지만

UNLINK 명령은 키를 데이터베이스에서 제거하지만 즉시 값을 삭제하지 않습니다. 대신 값은 나중에 백그라운드에서 비동기식으로 삭제됩니다.
이로 인해 DEL 명령보다 훨씬 빠를 수 있습니다.

DEL 명령은 키를 데이터베이스에서 제거하고 즉시 값을 삭제합니다. 이것은 UNLINK 명령보다 느릴 수 있지만 값을 즉시 삭제해야
하는 경우 더 나은 옵션일 수 있습니다.

전반적으로 UNLINK 명령은 값을 즉시 삭제할 필요가 없고 성능을 향상시키고 싶다면 더 나은 옵션입니다. DEL 명령은 값을 즉시 삭제해야 하는 경우 더 나은 옵션이다..! 👍🏼

[MONGO DB] Query Targeting: Scanned Objects / Returned has gone above 1000

ALERT	
Query Targeting: Scanned Objects / Returned has gone above 1000
 
The ratio of documents scanned to returned exceeded 1000.0 on  XXXX.com, 
which typically suggests that un-indexed queries are being run. 
To help identify which query is problematic, we recommend navigating to the
Query Profiler tool within Atlas. Read more about the Profiler here.

다음과 같은 경고문이 발견되었다. 다음 경고문은 말그대로

Scanned value가 1000 이상이 될때 발생하는 이슈(?)이다.

해결법은 당연하게도 인덱스를 거는것인데 비즈니스 로직상 건다면 array에 걸어야하는 문제가 있었다

다행이도 mongodb에서는 array index를 지원해주기도 하고

https://www.mongodb.com/docs/manual/core/indexes/index-types/index-multikey/create-multikey-index-basic/

해당 비즈니스 로직은 caching처리를 하니 live환경에서도 이제 다음과 같은 이슈는 해결되었다. 또한 compound index를 걸기로 비즈니스 로직에서 해결하였다.

추가적으로 exsits는 현재 2024년 2월(MongoDB 7.0) 기준 true일때만 인덱스가 동작한다. false일때 동작하지 않음

image

https://www.mongodb.com/docs/manual/reference/operator/query/exists/#use-a-sparse-index-to-improve--exists-performance

[ThreadLocal] Webflux를 쓰면서 벌어진 일

기존 spring boot starter web에서는 tomcat을 사용하고
Request Per Thread 모델이므로 요청이 들어오고 응답이 나갈때까지 동일한 Thread 가 요청을 처리하기 때문에 ThreadLocal 을 사용해도 문제가 없었다. (계정계에서 받아온 정보들을 담고 있었음)

webflux를 사용하면서 netty로 변경되었고 이 모델 에서는 하나의 요청을 처리하는 Thread 가 계속 변경될 수 있기 때문에 ThreadLocal 에 저장된 값이 없거나 뒤죽박죽 변경될 수 있었고 이에 이슈가 발생했었다.

비슷한 대안으로 Reactor Context를 사용할 수 있었고 이를 사용하여 해결하였는데 사용 예시는 다음과 같다 (회사 코드 X)

@Component
public class ContextSettingFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        return chain.filter(exchange)
            .contextWrite(Context.of("requestId", exchange.getRequest().getId()));
    }
}

실제 커머스에서는 이러한 상황에 큰 장애가 났다고 하다 (cj 계열)
주의하자

REFERENCE

spring data mongo를 사용할때 낙관락 이슈

궁금증

회사에서 mongodb를 특정ID(PK가 아닌)로 조회 후 없다면 생성후 저장하는 로직이 있는데 duplicated key exception이 발생하였다

mongoDB를 단순 저장하는 기능은 upsert기능을 제공할텐데 왜 그럴까?

결론

결론은 @Version에 관련된 이슈가 존재했다

해당 관련된 이슈들은 어느 정도 알려진 이슈였음

Persistable을 구현하였고 isNew() 메소드를 직접 구현하여 해결

Spring Boot 3.0 이상 Kotlin 프로젝트에서 querydsl 세팅하는 방법

spring data jpa는 이미 의존성 추가가 되어 있다는 가정하에

plugins{
... 생략
    kotlin("kapt") version "1.7.22"
}

dependencies{ 
... 생략
    implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
    kapt("com.querydsl:querydsl-apt:5.0.0:jakarta")
}

를 추가 해주면 된다.

간혹 인터넷에서

dependencies{ 
    implementation("com.querydsl:querydsl-jpa:5.0.0")
    kapt("com.querydsl:querydsl-apt:5.0.0")
}

를 추가 해주기만 하면 된다고 하는데 3.0 버전에서는 javax -> jakarta로 패키지가 변경되었으므로 이를 추가 적으로 :jakarta를 명시 해주어야 한다.

(2023년 4월 2일 기준으로 작성 앞으로 라이브러리 버젼 등에 따라 상이해질 수 있음 😿 )

고로 정리하자면 build.gradle.kts 다음과 같을 것이다.

plugins {
    id("org.springframework.boot") version "3.0.5"
    id("io.spring.dependency-management") version "1.1.0"
    kotlin("jvm") version "1.7.22"
    kotlin("plugin.spring") version "1.7.22"
    kotlin("plugin.jpa") version "1.7.22"
    kotlin("kapt") version "1.7.22"

}

group = "me.saechimdaeki"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17

allOpen {
    annotation("javax.persistence.Entity")
    annotation("javax.persistence.MappedSuperclass")
    annotation("javax.persistence.Embeddable")
}

repositories {
    mavenCentral()
}

dependencies {
    ... 생략
    implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
    kapt("com.querydsl:querydsl-apt:5.0.0:jakarta")
    runtimeOnly("com.h2database:h2")
}

[kafka] If you believe this class is safe to deserialize, please provide its name. If the serialization is only done by a trusted source, you can also enable trust all

문제발생

서버 A와 서버 B의 Payload 모델 규격은 동일한 상황에서 서버 A에서 카프카 브로커에 메시지를 produce했을때 서버B에서
payload 모델을 consume했는데 다음과 같은 에러가 발생

If you believe this class is safe to deserialize, please provide its name. If the serialization is only done by a trusted source, you can also enable trust all (*)

발생 원인 :

카프카 메세지를 deserialize 할 때 헤더의 값에 패키지명이 포함되어 있다.

즉, producer와 consumer에서 같은 동일 모델을 payload로 사용한다 해도 패키지명이 달라서 생겼던 이슈

해결방법은 3가지가 있다.

  1. 먼저 가장 간단하게는 패키지명 또한 통일한다
  2. 헤더 검사를 실행하지 않겠다는 옵션을 consumerfactory 빈 설정에 추가한다
public ConsumerFactory<String, KafkaPayloadModel> originConsumerFactory() {
       return new DefaultKafkaConsumerFactory<>(createPropMap(),
                                                new StringDeserializer(),
                                                new JsonDeserializer<>(KafkaPayloadModel.class,false));
   }
  1. 모든 패키지를 신뢰하겠다는 옵션을 준다
public ConsumerFactory<String, KafkaPayloadModel> originConsumerFactory() {
    JsonDeserializer<KafkaPayloadModel> kafkaPayloadModelJsonDeserializer = new JsonDeserializer<>(KafkaPayloadModel.class);
    kafkaPayloadModelJsonDeserializer.addTrustedPackages("*");
 
    return new DefaultKafkaConsumerFactory<>(createPropMap(),
                                             new StringDeserializer(),
                                             kafkaPayloadModelJsonDeserializer);
}

KafkaStreams TimeWindows.of 를 최신으로 변경하는 방법

.windowedBy(TimeWindows.of(Duration.ofMinutes(5)).grace(Duration.ofSeconds(30))))

기존에 이런식으로 고정 창 크기가 5분이고 유예 기간이 30초인 TimeWindows 개체를 만드는 방법이 있었다.

https://kafka.apache.org/27/javadoc/org/apache/kafka/streams/kstream/TimeWindows.html

이 방법은 deprecated가 되었음을 알 수 있는데 유예기간이 있게 설정하고 싶다면 다음과 같이할 수 있다

.windowedBy(TimeWindows.ofSizeAndGrace(Duration.ofMinutes(5),Duration.ofSeconds(30)))

유예기간이 없게 설정하고 싶다면 이 또한 명확한 메소드가 제공되는데 다음과 같다

.windowedBy(TimeWindows.ofSizeWithNoGrace(Duration.ofMinutes(5)))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.