반응형

페이지 모달에서 에니메이션이 동작하지 않는 것이다.

원인은 DOM 추가와 active 클래스 추가가 동시에 일어나서 CSS transition 이 작동하지 않았음

그래서 requestAnimationFrame() 으로 해결했는데

혹시나 해서 과거 코드의 참 많이 쓰기도 했고 해악이기도 했었던 sleep(0) 이런거랑 같은거인지를 물어보았다.

아니라고 합니다.

뭐라고 하는지 들어보려면 아래를 확인

 

⏺ requestAnimationFrame은 단순한 sleep 함수가 아니라 브라우저의 렌더링 사이클과 동기화되는
  특별한 함수입니다.

  🎨 브라우저 렌더링 사이클

  브라우저는 다음 순서로 화면을 그립니다:
  1. JavaScript 실행
  2. Style 계산 (CSS 적용)
  3. Layout (요소 크기/위치 계산)
  4. Paint (픽셀 그리기)
  5. Composite (레이어 합성)

  이 과정은 보통 60fps로 실행됩니다 (16.67ms마다 1번).

  ⏰ setTimeout vs requestAnimationFrame

  setTimeout(fn, 10)

  document.body.appendChild(element);
  setTimeout(() => {
      element.classList.add('active');
  }, 10);

  - 임의의 시간 후 실행
  - 렌더링 사이클과 무관하게 실행
  - 10ms 후 실행되지만, 렌더링 중간에 실행될 수도 있음
  - 불필요한 렌더링을 유발할 수 있음

  requestAnimationFrame(fn)

  document.body.appendChild(element);
  requestAnimationFrame(() => {
      element.classList.add('active');
  });


  - 다음 렌더링 직전에 실행
  - 브라우저가 "이제 그릴 준비됐어!"라고 할 때 실행
  - 렌더링 사이클과 완벽하게 동기화
  - 부드러운 애니메이션 보장


  🔄 왜 두 번 호출하나요?

  requestAnimationFrame(() => {
      requestAnimationFrame(() => {
          element.classList.add('active');
      });
  });


  이유:
  1. 첫 번째 RAF: DOM에 요소 추가 후 첫 렌더링 대기
    - 요소가 DOM 트리에 추가됨
    - 초기 스타일 계산 (opacity: 0, scale: 0.9)
    - 화면에 그려짐 (보이지 않는 상태)
  2. 두 번째 RAF: 초기 상태가 확정된 후 실행
    - 이제 transition 시작점이 확정됨
    - active 클래스 추가
    - CSS transition 애니메이션 시작

  📊 타이밍 비교

  // ❌ 즉시 실행 - 애니메이션 없음
  element.appendChild();
  element.classList.add('active'); // 0ms - 초기 상태가 없어서 transition 안됨

  // ⚠️ setTimeout - 불안정
  element.appendChild();
  setTimeout(() => {
      element.classList.add('active'); // 10ms - 타이밍이 보장 안됨
  }, 10);

  // ✅ RAF - 안정적
  element.appendChild();
  requestAnimationFrame(() => {
      requestAnimationFrame(() => {
          element.classList.add('active'); // 다음 프레임 (약 16-33ms)
      });
  });


  🎯 핵심 차이점

  | 특징                             | setTimeout             | requestAnimationFrame |
  |---------             ---|-----------            --|-----------------------|
  | 실행 시점                  | 지정한 ms 후              | 다음 렌더링 직전             |
  | 렌더링 동기화            | ❌ 없음                      | ✅ 완벽 동기화              |
  | 성능                          | 불필요한 렌더링 가능  | 최적화됨                  |
  | 애니메이션 부드러움 | 떨림 가능                    | 항상 부드러움               |
  | 브라우저 최적화        | ❌                              | ✅ (탭 비활성 시 중지)        |

  💡 실제 사용 예시

  // 🎬 애니메이션이 필요한 경우
  requestAnimationFrame(() => {
      element.style.transform = 'translateX(100px)';
  });

  // ⏱️ 단순 지연이 필요한 경우
  setTimeout(() => {
      console.log('1초 후 실행');
  }, 1000);

  // 🎨 CSS transition 트리거
  element.style.display = 'block';
  requestAnimationFrame(() => {
      requestAnimationFrame(() => {
          element.style.opacity = '1'; // transition 작동
      });
  });



  결론: requestAnimationFrame은 브라우저의 그리기 주기와 완벽하게 동기화되어 애니메이션을
  부드럽고 효율적으로 만드는 전문 도구입니다!

 

🔎 CSS Transition 작동 과정

요소가 DOM에 추가됨
자바스크립트로 element.appendChild() 하거나, 서버에서 HTML을 내려주면 브라우저는 DOM(Document Object Model) 트리에 그 요소를 넣습니다.

브라우저가 초기 상태 렌더링
DOM에 들어온 요소는 CSSOM(CSS Object Model) 과 합쳐져서 렌더 트리를 구성합니다.

이때 적용되는 클래스/스타일 중에서 “시작 상태(start state)”가 반영됩니다. (예: opacity: 0)

reflow & repaint 발생
브라우저가 레이아웃을 다시 계산하고(reflow), 픽셀 단위로 화면에 그립니다(repaint).

즉 “초기 상태”를 사용자 화면에 그린 거예요.

클래스가 추가되어 최종 상태로 변화 요청
자바스크립트에서 element.classList.add("active") 같은 동작을 하거나, CSS 상태 변화(:hover 등)가 발생합니다.

CSS transition 이 정의되어 있다면, 브라우저는 이전 값 → 새로운 값 사이를 일정 시간에 걸쳐 보간(interpolate)해서 애니메이션합니다.

예: opacity: 0 → opacity: 1 으로 0.5초 동안 변화.

Transition 실행
이 과정에서 브라우저는 중간 단계의 스타일을 계산하면서 매 프레임마다 repaint 합니다.

개발자가 지정한 transition-duration, transition-timing-function 값에 따라 부드럽게 변화합니다.

👉 즉, CSS Transition 은 “상태 변화”가 감지된 순간, 브라우저가 현재 상태와 목표 상태 사이를 보간해서 화면에 반영하는 것

💡 핵심 정리
DOM에 추가될 때 이미 최종 클래스를 붙여버리면 브라우저는 “초기 상태를 렌더링할 기회”를 갖지 못합니다 → transition이 눈에 띄지 않고 바로 최종 상태로 그려져 버림.

그래서 보통은:

element.classList.add("hidden");   // 초기 상태
document.body.appendChild(element);
requestAnimationFrame(() => {
  element.classList.add("active"); // transition 발동
});


이런 식으로 한 프레임 늦게 최종 상태 클래스를 붙여서 transition 을 보장합니다.

반응형
반응형

요즘 연일 AI 서비스들이 쏟아져나오고 있다.

빅테크부터 스타트업까지 정말 다양한 서비스들을 내놓고 있다. 

이제 Chat GPT, Gemini, Grok, Claude 등을 종종 다양하게 써보며 구글검색을 주로 곁들여 쓰고 있다

특히 요즘 구글검색이 너무 사용하기 좋게 변했던데 첫페이지에서 왠만한 쓸만한 답변을 다 얻고 있어서

페이지 넘겨가며 검색하는 경우가 거의 없을 정도로 줄었다.

진짜 1년전과 비교해도 검색시간이 훨씬 단축된게 심각하게 체감될 정도다.

 

워낙 다양한 AI 툴들을 써보려고 노력하고 있고, 뭔가 새로나왔다고 하면 한번씩은 써보고 하는데

작년말부터 알게된 Goover AI 는 국산이라고 해서 그래도 애정을 가지고 한번씩 사용해보는데

오늘 정말 실망스러운 리서치 결과를 보고 참 고민스럽다.

계속 쓰는게 맞는건가... 결국 빅테크인건가

 

기업공부를 위해 디앤디파마텍 회사에 대해서 조사해달라고 넣었는데 내용이 너무 별로인거다.

차마 캡쳐는 하지 않았다. 요즘 증권사 리포트에서 중요하게 언급되는 이야기는 하나도 안나오길래 재차 질문했고, 계속 빙빙 돌려말하는 것같은 답변만 내놓는다. 그래서 GLP-1 기술에 대해서 설명해달라는 언급을 하니까 그제서야 관련 언급을 하고있다. 먼가 좀 쎄한 느낌

근데 답변중에 최근에 GLP-1 기술의 파킨슨병 치료제 임상2상을 시작한다는데 이건 뭐지. 내가 보고서에서 읽었던거랑은 완전 다른 내용이다.

내가 알기로는 파킨슨병 치료제는 개발중단된걸로 알고 있었는데 올해 임상2상을 진행한다고? 전혀 금시초문, 그래서 구글검색을 해봄

정확한 내용을 확인해보니 임상실패해서 중단되었던 NLY01 이 다시 재조명받고 있어 다시 진행 가능성여부에 대해 기대한 기사가 있었다. 

무튼, AI가 완전히 잘못된 이야기를 전달해주는 것 같아서 다른 AI서비스들을 다 똑같이 질문해봤는데 다 정확히 대답해주고 Goover AI만 일단 이렇게 오류가 있는중. 

결국 해당 내용의 레퍼런스를 클릭해보니 날짜정보가 잘못되었던게 원인인것 같다. 해당 내용 끝의 6이라는 숫자 레퍼런스를 클릭해봄.

2019년 기사를 레퍼런스로 했던 것이었음. 

이거이거 레퍼런스의 날짜를 인식하는 루틴에 오류가 있는것 같아

결국 아직 시기상조인가보다. 

 

검색해보니 실제로 2019년에 임상 2상 진행시작 결국 2023년 임상실패로 개발중단.

단 60세미만의 젊은 환자에게 유의미한 변화가 보여 다시 무언가 해보려는 듯한 기대감이 있는 정도인듯하다.

구글 및 재미나이, 그록, 챗지피티 모두 유사한 답 

 

AI 서비스를 사용할 때 항상 오류가 있을 수 있으므로 경각심을 가지고 레퍼런스 체크를 잘 해보자

투자는 본인의 결정이고 책임은 오롯이 자신이 지는 것이기 때문.

반응형
반응형

✅ 가비아 DNS 그대로 사용하는 방식

장점

  • 설정이 간단: EC2 퍼블릭 IP만 입력하면 끝
  • 추가 비용 없음: Route 53을 쓰지 않으니 AWS 요금 없음
  • 관리 분산: 도메인 만료/갱신은 가비아에서, 서버 관리는 AWS에서

단점

  • DNS 기능 제한적: 가비아 DNS는 단순 A/CNAME 정도만 제공 → 세밀한 트래픽 라우팅, 복잡한 레코드 관리 불편
  • 전파 속도 조금 느림: 가비아 네임서버 특성상 DNS 전파가 AWS보다 느릴 수 있음
  • AWS 서비스 연계 어려움: CloudFront, ALB, S3 웹호스팅 등을 붙일 때 Route 53보다 설정이 번거로움

✅ Route 53 사용하는 방식

장점

  • AWS 서비스와 강력한 연동:
    • CloudFront, ALB(로드밸런서), S3 정적 웹호스팅 등 쉽게 연결
    • 헬스체크, 지리적 라우팅(서울은 서울 서버, 미국은 미국 서버 등) 가능
  • 빠른 DNS 응답 속도: AWS 글로벌 인프라 기반으로 성능 우수
  • 고급 기능 제공: 가중치 라우팅, 페일오버, 지연시간 기반 라우팅 등 엔터프라이즈 기능

단점

  • 추가 비용 발생:
    • 호스팅 존 유지비 약 $0.50 / 월
    • 쿼리당 소량 비용 (트래픽 많을 때만 체감)
  • 초기 설정 복잡: 가비아에서 네임서버 변경해야 함
  • 관리 이원화: 도메인 자체는 가비아에서 갱신, DNS는 AWS에서 관리

👉 정리

  • 개인 블로그/소규모 프로젝트 → 가비아 DNS 직접 사용 (간단, 무료)
  • 서비스 규모가 크거나 AWS 서비스 적극 활용 예정 → Route 53 권장 (확장성, 성능, 유연성)
반응형
반응형

서버 디스크 공간 문제를 해결하기 위해 Docker 관련 정리 명령

  해결 방법:

  1. 사용하지 않는 Docker 이미지 정리:
  docker system prune -a --volumes

  2. Docker 빌드 캐시 정리:
  docker builder prune -a

  3. 디스크 사용량 확인:
  df -h
  du -sh /var/lib/docker/*

  4. APT 캐시 정리:
  sudo apt-get clean
  sudo apt-get autoremove

  5. 로그 파일 정리:
  sudo journalctl --vacuum-time=3d

  이런 정리 작업 후에 Docker 빌드를 다시 시도해보시기 바랍니다.

반응형
반응형

N+1 문제와 1+N 문제는 사실상 같은 문제를 다르게 표현한 것입니다.

데이터베이스 성능 최적화에서 가장 흔히 발생하는 문제 중 하나입니다.

 

N+1 문제란?

하나의 메인 쿼리 + N개의 추가 쿼리가 실행되는 문제입니다.

구체적인 예시

시나리오: 사용자 10명과 각자의 게시글을 조회

 
 
sql
-- 1. 첫 번째 쿼리: 사용자 목록 조회
SELECT * FROM users LIMIT 10;

-- 2. 각 사용자마다 개별 쿼리 실행 (N번)
SELECT * FROM posts WHERE user_id = 1;
SELECT * FROM posts WHERE user_id = 2;
SELECT * FROM posts WHERE user_id = 3;
...
SELECT * FROM posts WHERE user_id = 10;

결과: 총 11개의 쿼리 (1 + 10)

코드에서 발생하는 예시

 
 
python
# 문제가 있는 코드
users = User.objects.all()[:10]  # 1번째 쿼리

for user in users:
    posts = user.posts.all()     # 각 사용자마다 추가 쿼리
    print(f"{user.name}: {len(posts)} posts")
 
 
javascript
// JavaScript/Node.js 예시
const users = await User.findAll({ limit: 10 });  // 1번째 쿼리

for (const user of users) {
    const posts = await Post.findAll({             // N번의 추가 쿼리
        where: { userId: user.id }
    });
    console.log(`${user.name}: ${posts.length} posts`);
}

성능에 미치는 영향

 
 
사용자 10명: 11개 쿼리
사용자 100명: 101개 쿼리  
사용자 1000명: 1001개 쿼리

데이터가 증가할수록 쿼리 수가 선형적으로 증가하여 성능이 급격히 저하됩니다.

해결 방법들

1. JOIN 사용 (가장 기본적)

 
 
sql
-- 하나의 쿼리로 해결
SELECT u.*, p.* 
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
WHERE u.id <= 10;

2. Eager Loading

 
 
python
# Django ORM
users = User.objects.prefetch_related('posts')[:10]

# SQLAlchemy
users = session.query(User).options(joinedload(User.posts)).limit(10)
 
 
javascript
// Sequelize
const users = await User.findAll({
    limit: 10,
    include: [{ model: Post, as: 'posts' }]
});

3. 배치 조회

 
 
python
# 두 번의 쿼리로 해결
users = User.objects.all()[:10]
user_ids = [user.id for user in users]
posts = Post.objects.filter(user_id__in=user_ids)

# 메모리에서 매핑
posts_by_user = {}
for post in posts:
    if post.user_id not in posts_by_user:
        posts_by_user[post.user_id] = []
    posts_by_user[post.user_id].append(post)

4. DataLoader 패턴 (GraphQL에서 주로 사용)

 
 
javascript
const DataLoader = require('dataloader');

const postLoader = new DataLoader(async (userIds) => {
    const posts = await Post.findAll({
        where: { userId: userIds }
    });
    
    // userIds 순서대로 정렬하여 반환
    return userIds.map(id => 
        posts.filter(post => post.userId === id)
    );
});

1+N과 N+1의 차이점

표현 방식의 차이일 뿐 본질적으로는 동일합니다:

  • N+1: N개의 데이터를 위해 1개의 메인 쿼리 + N개의 추가 쿼리
  • 1+N: 1개의 메인 쿼리 + N개의 추가 쿼리

둘 다 같은 문제를 설명하며, N+1이 더 일반적으로 사용되는 표현입니다.

실무에서 주의할 점

숨겨진 N+1 문제

 
 
python
# 언뜻 문제없어 보이지만...
users = User.objects.select_related('profile').all()

for user in users:
    # 여기서 추가 쿼리 발생!
    recent_posts = user.posts.filter(created_at__gte=last_week)

ORM의 지연 로딩

대부분의 ORM은 기본적으로 지연 로딩(Lazy Loading)을 사용하므로, 의도치 않게 N+1 문제가 발생할 수 있습니다.

N+1 문제는 애플리케이션 성능에 치명적인 영향을 미칠 수 있으므로, 개발 단계에서부터 쿼리 최적화를 고려하고 데이터베이스 쿼리 로그를 모니터링하는 것이 중요합니다.

반응형

+ Recent posts