Edit - Project Setting - Physics - Gravity 변경을 통해 중력값 변경으로 여러가지 표현 가능
(x 또는 z에 값을 줄시 오브젝트가 바람을 맞는듯한 효과, y값을 -4로 하면 달의 중력과같게)

 

꽤 재미있었다

 

Simple Use

using System;

public class SomeManager {
    private Lazy<SomeClass> lazyLoadedObject = new Lazy<SomeClass>(() => new SomeClass());

    public SomeClass LazyLoadedObject {
        get { return lazyLoadedObject.Value; }
    }
}

public class SomeClass {
    // SomeClass의 내용
}

 

 

초기화를 개발자가 사용하는 시점까지 미루고 이후 필요할때 로드하는 방식이다.

 

Lazy Loading 패턴을 사용하는 이유 중 하나는 성능 측면에서의 이점을 얻기 위해서다.

특히 초기화 시점에서 리소스를 할당하거나 계산을 하고 싶지 않거나, 해당 리소스가 필요한 경우가 제한적인 경우에 Lazy Loading을 사용한다.

어떤 멤버 변수나 리소스를 선언 시 초기화하는 것은 그 자체로 큰 성능 이슈를 일으키진 않는다.

그러나 프로그램의 시작 시점이나 해당 멤버 변수가 사용될 때까지 초기화할 필요가 없는 경우, 초기화를 미루는 것이 성능적으로 유리할 수 있다.

예를 들어, 어떤 멤버 변수가 초기화되고 계산에 많은 시간이 걸린다면, 해당 변수를 Lazy Loading 패턴으로 초기화하는 것이 사용자 경험과 성능을 향상시킬 수 있다. 사용자가 해당 변수를 실제로 요청할 때까지 초기화를 미루고, 필요한 경우에만 리소스를 할당하고 계산을 수행할 수 있다.

그러나 모든 상황에서 Lazy Loading을 사용하는 것이 항상 최적은 아니다. 필요한 리소스를 너무 늦게 초기화하면 사용자 경험이 저하될 수 있다. 따라서 Lazy Loading을 사용할 때는 초기화를 늦추는 것과 성능 향상 사이에서 적절한 균형을 유지하는 것이 중요하다.

'Development > C#' 카테고리의 다른 글

Cashing  (0) 2023.12.27
Object pooling  (0) 2023.12.27

Simple Used Sample

public class DataFetcher {
    private SomeData cachedData;

    public SomeData GetSomeData() {
        if (cachedData == null) {
            // 캐시된 데이터가 없는 경우에만 데이터를 가져옴
            cachedData = FetchDataFromServer(); // 데이터를 가져오는 시간이 오래 걸리는 작업
        }

        return cachedData; // 캐시된 데이터 반환
    }

    private SomeData FetchDataFromServer() {
        // 서버에서 데이터를 가져오는 작업 (시뮬레이션을 위한 임의의 데이터 생성)
        SomeData data = new SomeData(); // 실제로는 서버 통신 등의 작업을 수행
        return data;
    }
}

public class SomeData {
    // 데이터 구조 정의
}

 

'Development > C#' 카테고리의 다른 글

Lazy Loading Pattern  (0) 2023.12.27
Object pooling  (0) 2023.12.27

Pool Create

using UnityEngine;
using System.Collections.Generic;

public class ObjectPool : MonoBehaviour
{
    public GameObject prefabToPool;
    public int poolSize = 10;

    private List<GameObject> objectPool = new List<GameObject>();

    private void Start()
    {
        InitializeObjectPool();
    }

    private void InitializeObjectPool()
    {
        for (int i = 0; i < poolSize; i++)
        {
            GameObject obj = Instantiate(prefabToPool);
            obj.SetActive(false);
            objectPool.Add(obj);
        }
    }

    public GameObject GetPooledObject()
    {
        foreach (GameObject obj in objectPool)
        {
            if (!obj.activeInHierarchy)
            {
                obj.SetActive(true);
                return obj;
            }
        }

        // 오브젝트 풀에 사용 가능한 오브젝트가 없는 경우
        GameObject newObj = Instantiate(prefabToPool);
        newObj.SetActive(true);
        objectPool.Add(newObj);
        return newObj;
    }

    public void ReturnObjectToPool(GameObject obj)
    {
        obj.SetActive(false);
    }
}

 

 

Using Sample

public class YourScript : MonoBehaviour
{
    private ObjectPool objectPool;

    private void Start()
    {
        objectPool = GetComponent<ObjectPool>();
    }

    private void SpawnObjectFromPool()
    {
        GameObject newObj = objectPool.GetPooledObject();
        // 가져온 오브젝트를 사용 (위치 설정 등)
        newObj.transform.position = Vector3.zero;
    }

    private void ReturnObjectToPool(GameObject obj)
    {
        objectPool.ReturnObjectToPool(obj);
    }
}

'Development > C#' 카테고리의 다른 글

Lazy Loading Pattern  (0) 2023.12.27
Cashing  (0) 2023.12.27

커서 기반 무한 스크롤을 구현해보려는데 프론트 단을 할 줄 몰라서 그냥 스프링에서 로직 처리를 했다

 

Repository DI 시키는 부분 아래에다가 그냥 뿌려준 List lastId 값을 전역화 시켜서 API 요청이 올때마다 Get으로는 lastId를 초기화, 스크롤 이벤트 리스트 추가 요청 Post로는 lastid가 0이 아닌 경우를 제어문으로 처리한 뒤 ++ 해주게 만들었는데 잘 동작한다

 

js는 그냥 스크롤 이벤트만 확인해서 요청을 보내게 처리했다

 

해놓고 보니 뭔가 MVC 패턴도 깨지는 것 같은 느낌이고 View 단에서 처리할 로직을 가져온 것 같긴한데 프론트 개발자와 협업하게 된다면 불필요한 로직이 사라지고 lastID만 가지고 와서 커서 기반으로 Where 절을 써서 리스트를 날려주면 되니 더 쉬울 것 같다

 

OFFSET 메소드 자체가 데이터 테이블의 row 수가 늘어날수록 skip할 index 분량이 많아져 성능이 구려지다 보니 대용량 데이터 처리 연습은 커서 기반으로 SEQ을 어떻게 Where절 조건식으로 날려 원하는 인덱스를 바로 타게할까 고민하면서 짜는게 맞는 것 같다

 

굳이 또 나중에 서버 단에서 무한스크롤 로직 구현을 해서 리스트를 뿌려줘야 한다면 나중에는 그냥 깔끔하게 객체를 하나 만들어서 import 시킬 것 같다

(repository 사이에 primitive 타입 변수가 선언 되어있으니 꼴보기 싫음 ㅋ)

 

그 외 테스트용 메소드 타임체크 및 로그 저장 AOP와 슬로우쿼리 발생 시 개발자 연락처로 발송하는 백오피스용 기능들을 만들어서 넣어봤는데, 당장은 크게 활용처가 없지만 서비스가 커질수록 유용할 것 같다. 만들면서 재밌기도 했고.

'WIL' 카테고리의 다른 글

#11  (0) 2022.12.05
#10  (0) 2022.11.27
#9  (0) 2022.11.20
#8  (1) 2022.11.14
#7  (0) 2022.11.07

ELK ( ElasticSearch, LogStash, Kibana) 스택을 써서 MySQL의 1600만건이 넘는 책 데이터를

Grok 필터 플러그인을 써서 마이그레이션 해서 테스트 중이다

(1630만건 데이터 Logstash로 마이그레이션 하는데 대략 51분 43초 정도 걸렸다)

 

Date의 경우 filter에서 String 값을 ISO 기준 Date 형태로 변환해줘서 넣어주긴했는데 애초에 발간년도, 등록년도, EL 등록TimeStamp 등으로 나눠지니 컨트롤하기 번거롭긴 하다

(그냥 epoch_millis Type 정도로 해두고 로그조사 페이지 만들때처럼 서버단에서 SimpleFormat 해줄까 하는 생각도 듦)

 

리턴되는 Row 값이 많아질 수록 확실히 엘라스틱서치가 빠르긴 한데

(대략 10만 Row당 Fulltext Indexing한 MySQL은 0.400 mills, 노리 형태소 분석기로 분석한 엘라스틱서치는 0.050 mills 정도 나온다)

 

책 데이터, 그 중에서도 더럽기 이를데 없는 Title 필드는 굉장히 세팅을 하기가 힘들었다

 

nori 형태소 분석기를 사용하는데 먼저 책의 경우 전체 타이틀명을 검색할 경우를 생각해서

nori analyzer의 decompound_mode를 mixed로 설정했는데 책 타이틀 특성 상 원체 나눠지는 term이 많다보니

max_shingle_size의 default max 값인 3으로는 안될 것 같았다

(전체 텀을 합친 기존 검색어를 만들지 못해서 keyword 필드로 검색해야 하는 귀찮음이 발생한다. 사용자 경험에 악영향을 줄거라고 생각해서 다른 방법을 생각해보기로 했다. 물론 이부분도 서버단에서 다른 SEQ을 날려 multi field를 타게 하면 되겠지만, 아직 스프링단에서 EL로 expression 쿼리 날리는건 익숙하지가 않다)

 

그래서 따로 index.max_shingle_diff 값을 세팅을 통해 5정도로 설정해주고

(term_vector 로 1600만 데이터 타이틀 색인으로 엄청 느려질게 무서워서 5정도만 했다. 근데 5도 row 한개 당 termvector에 쌓이는 데이터들을 보니 무서움)

 

max_result_window 값 default가 10000밖에 안되길래 row가 많을 때 1만개만 불러와서 RDB와의 속도 비교 테스트가 안되서 100만 정도로 쿨하게 풀어줌

(실제 상용화 서비스를 진행할 때는 물론 속도나 ux를 위해 default 값 또는 그보다 낮게 두고 서버 단에서 page 쪽에 limit를 걸어줄 생각이다)

 

쨋든 이런식으로 인덱스를 만들어서 데이터를 넣고 테스트 했는데, 이놈이 또 analyze를 노리 분석기로 하니까 검색 단어를 멋대로 쪼개서 검색해버림 (전체 타이틀을 검색하면 그 검색어를 자기 멋대로 쪼갬;)

 

그래서 그냥 색인만 nori로 하고 검색 analyze는 유저나 내가 생각하기 편하게 standard로 나눠준 뒤 인덱스를 새로 만들어서 테스트 해봤다

원하던대로 풀 타이틀, 쪼갠 글자들로 검색이 잘되긴 한다

(max_shingle_size 필터를 5까지 풀어서 term을 최대 5개까지 합쳐서 색인 termvector를 만들도록 했는데 안되면 이상하다. 나으 귀중한 컴퓨터 리소스를 그토록 쳐먹게 만들었는데...)

 

문제는 편하게 하려고 서버단에서 jpa로 contains 검색을 통해 EL로 SEQ을 날리는데 띄어쓰기가 들어가면 contains 메소드가 오류를 뿜어냄

 

처음에는 서버단에서 Criteria 등을 사용해 확장 쿼리를 날릴까 하다가 stackoverflow에도 같은 문제로 "당신 덕분에 해결됬어요 고마워요!" 하는 답댓이 없길래, 삽질이 될 것을 우려해서 그냥 View단에서 받은 검색어 String의 빈 공백을 서버에서 포멧팅하고 EL DB 자체적으로 Term 들을 shingle filter에서 합치는 작업에서 token_seperator로 공백문자열을 없애서 텀들을 합치게 만들었다.

(이렇게 하면 유저들은 띄어쓰기를 했지만 DB는 띄어쓰기가 없는 텀을 검색하게 될 테니까 ㅇㅅㅇ)

 

이렇게 띄어쓰기 문제는 해결했는데 막상 해보니까 느리다

색인이 너무 많아져서 그런 것 같아서 타이틀 필드에서 현재 성능과 검색 편의성의 사이에서 계속해서 인덱스 설정이나 맵핑을 어떻게 할 지 조율중이다

'WIL' 카테고리의 다른 글

#12  (0) 2022.12.16
#10  (0) 2022.11.27
#9  (0) 2022.11.20
#8  (1) 2022.11.14
#7  (0) 2022.11.07

 

자잘한 것들을 만들고 공부도 하면서 시간 보내고 있다

 

팀원들끼리 프로젝트에서 slow query 발생 시 smtp로 메일을 보내거나 슬랙으로 메시지를 보내고, 스케쥴러를 돌려서 openAPI에서 특정 시간대에 필요한 정보를 가져와 새로운 테이블에 insert하는 기능, sse 구독을 통해 새로운 데이터 등록 시 알람을 보내주거나, 접속한 유저 ip나 시간대 등을 남겨서 실무자가 로그 조회를 할 수 있도록 하는 등의 백오피스 기능들을  만들고 있다

 

일단 적용 해놓은 엘라스틱서치나 레디스 등을 사용할 곳을 찾아보려고 백오피스 기능을 만들어서 적용시키고 있는 건데 꽤 재밌음

 

데이터좀 많은 환경에서 여러가지 조인 방식이나 인덱싱 방법도 실험해보고 싶은데 더미 데이터로 환경을 구성해야 될 것 같은데 어떻게 해야할까 고민 중이다

 

해보고 싶긴 많긴한데 결국 이번 주는 주로 aop를 커스텀하거나 test 클레스를 만드는 게 생각보다 재밌어서 jmeter나 ngrinder 사용해서 부하테스트를 진행할 거리를 찾으면서 aop, unitTest 클레스를 좀 더 잘 만들고 다룰 수 있도록 할 예정임. 아니면 로그스태시 써서 rdb 데이터를 복사해서 조회 기능을 아예 엘라스틱서치로 추가해보는 것도 재밌을 것 같다

 

'WIL' 카테고리의 다른 글

#12  (0) 2022.12.16
#11  (0) 2022.12.05
#9  (0) 2022.11.20
#8  (1) 2022.11.14
#7  (0) 2022.11.07

+ Recent posts