Loading [MathJax]/jax/output/HTML-CSS/jax.js
no image
크림 kream 보관판매 매크로
🤖 BoPan Robot - 크림 보관판매 자동화 프로그램 안녕하세요! 크림 보관판매를 자동화해주는 프로그램입니다.📥 다운로드🖥️ Windows 사용자Windows 다운로드 https://drive.google.com/file/d/19RHsrWpviG9_udveEEnFrC7rbiZiT0Ix/view BoPan-Robot-Windows.zip drive.google.com 압축 해제 → BoPan Robot.exe 실행 → 바로 사용! ✅🍎 macOS 사용자macOS 다운로드 https://drive.google.com/file/d/1T5pcZV23mqKWHHm5YYaO7xbp0xU04oDW/view BoPan-Robot-macOS.zip drive.google.com압축 해제 후 터미널에서 한 번..
2025.07.28
no image
[2] Apache에서 Nginx로 갈아타기
💪 실전 마이그레이션 과정1단계: 기존 시스템 분석 및 측정bash# 기존 Apache 서버 정확한 측정echo "=== Apache 측정 (date)==="apachemem=(ps aux | grep httpd | grep -v grep | awk '{sum+=6} END {print sum/1024}')apache_proc=(ps aux | grep httpd | grep -v grep | wc -l)connections=(netstatan|grep:80|grepESTABLISHED|wcl)echo"ApacheMemory:{apache_mem} MB"echo "Apache Processes: ${apache_proc}"echo "Active Connectio..
2025.07.05
no image
[1] Apache에서 Nginx로 갈아타기
🔥 서버를 옮기기전 했던 고민회사에서 운영하던 웹사이트 및 API 서버가 느려지기 시작했다. 비용 절약을 위해 하나의 서버에서 웹사이트와 백엔드 API를 함께 돌리고 있었는데, 트래픽이 증가하면서 문제가 표면화되었고,이번에 서버를 옮기는 과정에 (nhn 하드웨어서버 -> navercloud) 개선을 해보도록 하였다.문제 상황의 구체적 모습:웹사이트 응답 속도 저하: 특히 이미지와 정적 파일 로딩 지연API 서버 응답 지연: JSON 응답이 평소 200ms에서 2-3초까지 늘어남서버 리소스 경합: 웹사이트 트래픽 증가 시 API 성능도 함께 저하상호 영향: API 호출이 많아지면 웹사이트도 느려지는 악순환ㄴ메모리 사용량 모니터링으로 확인한 문제점:ApacheRAM: 7.7G total, 5.7G used..
2025.07.05
no image
Flutter 앱 아이콘 간단히 설정하기(flutter_launcher_icons 사용)
플러터 앱 출시를 위해선 앱 아이콘을 변경을 해주어야한다. 아니면 기본 flutter 아이콘으로 앱이 나오기 때문이다.이럴 때 유용하게 사용할 수 있는 라이브러리가 flutter_launcher_icons 이다! https://pub.dev/packages/flutter_launcher_icons flutter_launcher_icons | Dart packageA package which simplifies the task of updating your Flutter app's launcher icon.pub.dev 1. 설치터미널에 해당 명령어를 입력핵서 설치한다2. 설정1) pubspec.yaml 설정Flutter 프로젝트의 pubspec.yaml에 아래와 같이 설정합니다:dev_dependenci..
2025.01.30
no image
플러터 초기 실행시 AGP 버전 맞지 않을때 해결법
┌─ Flutter Fix ────────────────────────────────────────────────────────────────────────────────────┐ │ [!] This is likely due to a known bug in Android Gradle Plugin (AGP) versions less than 8.2.1, │ │ when │ │ 1. setting a value for SourceCompatibility and │ │ 2. using Java 21 or above. │ │ To fix this error, please upgrade your AGP version to at least 8.2.1. The version of AGP that │ │ your pr..
2025.01.30
no image
Java Supplier와 Lazy Evaluation
Java Supplier와 Lazy Evaluation: 성능 최적화를 위한 사용법Java에서 Supplier는 매개변수를 받지 않고 값을 반환하는 함수형 인터페이스입니다. 이는 주로 필요할 때 값을 생성하거나 계산을 지연시키기 위한 목적으로 사용됩니다. 이번 글에서는 Supplier를 활용한 Lazy Evaluation(지연 계산)의 개념과 사용법, 그리고 이를 통해 얻을 수 있는 성능 최적화를 소개하겠습니다.Supplier란?Supplier는 Java의 함수형 인터페이스 중 하나로, get 메서드를 통해 값을 반환합니다. 다른 함수형 인터페이스와 달리 매개변수를 받지 않고 값을 반환한다는 특징이 있습니다.예제 코드:import java.util.function.Supplier;public class ..
2024.12.08
no image
실제 Android 디바이스에 Flutter 앱 테스트 방법
1. 필수 준비물맥북의 경우 C to C 케이블 필수2. Android 기기에서 개발자 모드 활성화Flutter 앱을 실제 Android 기기에 배포하기 위해서는 개발자 모드를 활성화하고, USB 디버깅을 허용해야 합니다.개발자 모드 활성화:설정 > 휴대전화 정보 또는 디바이스 정보로 이동합니다.휴대전화 정보 에서 소트웨어 정보에 들어갑니다빌드 번호를 7번 연속으로 탭합니다."개발자 모드가 활성화되었습니다"라는 메시지가 나타납니다.USB 디버깅 활성화:설정 > 개발자 옵션으로 이동합니다. (위의 설정을 하고 나면 생깁니다)USB 디버깅을 활성화합니다.https://blog.naver.com/skomj/222984901460 해당 사이트 참조이 과정을 통해 Android 기기가 디버깅 모드에서 컴퓨터와 통..
2024.09.25
no image
Mac에서 <bits/stdc++.h> 설정하는 방법
알고리즘 공부시에C++에서 자주 사용하는 헤더 파일을 Mac에서 기본적으로 사용할 수는 없어서 따로 설정을 통하여 추가를 합니다.참고로 헤더파일은 모든 표준 라이브러리를 한 번에 포함하는 파일자바와 다르게 C++은 컴파일러가 필요한 헤더 파일을 자동으로 찾지 않는다.또한 어느 라이브러리에서 제공되는지를 명확하게 표시해야 한다.따로 추가를 하지 않으면 해당 단어를 적었을 때 빨간줄이 뜰 것입니다. 1. clang 버전 확인하기Mac에서는 Xcode가 clang을 기본 C++ 컴파일러로 사용합니다. 특정 기능이나 헤더 파일은 clang의 버전과 호환성 문제를 일으킬 수 있으므로, 설치된 clang의 버전을 확인하는 것이 유용합니다.(다른 블로그들에는 특정 경로로만 들어가서 설치하라고 하는데 해당 경로로 ..
2024.09.21

동작 gif

🤖 BoPan Robot - 크림 보관판매 자동화 프로그램 

안녕하세요! 크림 보관판매를 자동화해주는 프로그램입니다.


📥 다운로드

🖥️ Windows 사용자

Windows 다운로드

 

https://drive.google.com/file/d/19RHsrWpviG9_udveEEnFrC7rbiZiT0Ix/view

 

BoPan-Robot-Windows.zip

 

drive.google.com

 

  • 압축 해제 → BoPan Robot.exe 실행 → 바로 사용!

🍎 macOS 사용자

macOS 다운로드

 

https://drive.google.com/file/d/1T5pcZV23mqKWHHm5YYaO7xbp0xU04oDW/view

 

BoPan-Robot-macOS.zip

 

drive.google.com

  • 압축 해제 후 터미널에서 한 번만 실행:
    xattr -rd com.apple.quarantine "/Users/사용자명/Downloads/BoPan Robot.app"
  • 이후 BoPan Robot.app 더블클릭 → 정상 실행!

🎯 간단 사용법

1️⃣ 로그인

  • 우측 상단 로그인 버튼 클릭
  • 크림 계정 정보 입력

2️⃣ 상품 검색

  • 제품명 입력 후 검색 버튼
  • 원하는 상품 선택

3️⃣ 자동화 시작

  • 상세정보 버튼 클릭
  • 사이즈 확인 후 🤖로봇시작 클릭
  • 자동으로 보관판매 신청 완료! 🚀

4️⃣ 모니터링

  • 로그창에서 진행상황 실시간 확인
  • 🛑로봇중지 버튼으로 언제든 중지 가능

⚠️ 주의사항

  • 무료 프로그램입니다
  • 안전한 프로그램이지만 보안 프로그램에서 경고 발생 가능
  • 미서명 앱으로 인한 정상적인 보안 알림
  • ⚠️ 사용은 본인 책임하에 이용해주세요

🔧 문제 해결

자주 묻는 질문

  • 로그인 안됨: 계정정보 재확인
  • 검색 안됨: 다른 제품명으로 재시도
  • 앱 실행 안됨: 보안설정에서 실행 허용

macOS 사용자 추가 도움

보안 경고가 계속 나타나면:

  1. 시스템 환경설정보안 및 개인정보 보호
  2. 일반 탭에서 "BoPan Robot 열기 허용" 클릭

💬 사용 후기 & 지원

2025년 7월 27일 기준 작동 잘됨

댓글로 잘 되는지 적어주시면 몇분 나중 추가 권한 드리겠습니다! 🎁

 

 

문의 및 지원

  • 💬 댓글로 문의해주세요!
  • 🐛 버그 신고 환영합니다
  • 💡 개선 아이디어 제안해주세요

💪 실전 마이그레이션 과정

1단계: 기존 시스템 분석 및 측정

bash

# 기존 Apache 서버 정확한 측정
echo "=== Apache 측정 $(date) ==="
apache_mem=$(ps aux | grep httpd | grep -v grep | awk '{sum+=$6} END {print sum/1024}')
apache_proc=$(ps aux | grep httpd | grep -v grep | wc -l)
connections=$(netstat -an | grep :80 | grep ESTABLISHED | wc -l)

echo "Apache Memory: ${apache_mem} MB"
echo "Apache Processes: ${apache_proc}"
echo "Active Connections: ${connections}"

# 성능 벤치마크 측정
ab -n 1000 -c 10 https://example.co.kr/

측정 결과 (Before):

  • 초당 처리 요청: 45 requests/sec
  • 평균 응답 시간: 220ms
  • Apache 메모리 사용량: 186MB (11개 프로세스)
  • 시스템 전체 메모리 사용률: 74%
  • 스왑 사용률: 85% (심각한 상태)

2단계: Naver Cloud에 Nginx 환경 구축

bash

# CentOS 7 환경에서 Nginx 설치
yum install -y nginx

# 기본 설정 최적화
vi /etc/nginx/nginx.conf

글로벌 최적화 설정:

nginx

# worker 프로세스 최적화
worker_processes auto;
worker_connections 1024;

# 압축 설정
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;

# 보안 헤더
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

3단계: 도메인별 설정 분리 - 웹사이트 vs API 최적화

웹사이트(example.co.kr) 설정 - 정적 파일 최적화:

nginx

# /etc/nginx/conf.d/website.conf
server {
    listen 80;
    server_name example.co.kr www.example.co.kr;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.co.kr www.example.co.kr;

    # SSL 설정
    ssl_certificate /etc/letsencrypt/live/example.co.kr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.co.kr/privkey.pem;

    # 정적 파일 직접 처리 (핵심 개선점!)
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
        root /var/www/html;
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # 동적 요청만 백엔드로
    location / {
        proxy_pass http://127.0.0.1:8081;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

API 서버(api.example.co.kr) 설정 - 성능과 응답 최적화:

nginx

# /etc/nginx/conf.d/api.conf
server {
    listen 80;
    server_name api.example.co.kr;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.example.co.kr;

    # 같은 SSL 인증서 사용 (관리 효율성)
    ssl_certificate /etc/letsencrypt/live/example.co.kr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.co.kr/privkey.pem;

    # API 전용 최적화 설정
    location / {
        proxy_pass http://127.0.0.1:8085;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # API 응답 최적화
        proxy_buffering off;
        proxy_cache_bypass 1;
        proxy_read_timeout 30s;

        # CORS 설정
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
        add_header Access-Control-Allow-Headers "Content-Type, Authorization";
    }
}

4단계: SSL 인증서 통합 관리

기존 문제점: 각 도메인별 개별 인증서로 관리 복잡성 증가

해결방법: 멀티도메인 인증서로 통합

bash

# 4개 도메인을 하나의 인증서로 관리
certbot certonly --webroot -w /var/www/html \
  -d example.co.kr -d www.example.co.kr \
  -d api.example.co.kr -d www.api.example.co.kr

# 자동 갱신 설정
crontab -e
0 2 * * 1 certbot renew --quiet && systemctl reload nginx

5단계: 무중단 마이그레이션 실행

DNS 기반 점진적 전환:

  1. 새 서버에서 완전한 테스트 완료
  2. DNS TTL을 300초로 단축
  3. 10% 트래픽부터 시작해서 점진적 증가
  4. 모니터링하며 100% 전환 완료

🎯 결과와 성과 측정

마이그레이션 완료 후 기대 효과

Nginx 도입으로 다음과 같은 개선사항들을 기대할 수 있습니다:

bash

# Nginx 서버에서 예상되는 메모리 사용량 측정
nginx_mem=$(ps aux | grep nginx | grep -v grep | awk '{sum+=$6} END {print sum/1024}')
nginx_proc=$(ps aux | grep nginx | grep -v grep | wc -l)

# 시스템 메모리 상태 개선 예상
free -h
ab -n 1000 -c 10 https://example.co.kr/

: 186.062 MB

[root@serverhosting22-203 ~]# /usr/local/apache/bin/httpd -v Server version: Apache/2.4.33 (Unix) Server built: Feb 13 2019 20:00:47

Nginx 서버 (변경 후 - Nginx/1.20.1)

[root@enha-web-01 ~]# ps aux | grep nginx | grep -v grep | awk '{sum+=$6} END {print "Nginx Total: " sum/1024 " MB"}' Nginx Total: 66.582 MB

[root@enha-web-01 ~]# nginx -v nginx version: nginx/1.20.1


**시스템 메모리 상태 개선:**
```bash
# Before (Apache)
RAM: 7.7G total, 5.7G used (74% 사용률)
Swap: 3.9G total, 3.3G used (85% 사용률) ← 심각한 문제

# After (Nginx)
Mem: 15Gi total, 10Gi used (67% 사용률)
Swap: 0B ← 스왑 완전히 비활성화!
Available: 5.0Gi ← 실제 사용 가능한 메모리

예상 개선 효과 (Nginx 도입 시)

Nginx의 아키텍처 특성을 바탕으로 다음과 같은 개선을 기대할 수 있습니다:

  • 메모리 사용량: 186MB → 약 60-80MB (60% 이상 감소 예상)
  • 프로세스 수: 11개 → 5개 (1개 master + 4개 worker)
  • 동시 처리 능력: 현재의 3-5배 향상
  • 스왑 사용량: 85% → 0% (완전 제거)
  • 응답 시간: 평균 응답 시간 50% 이상 개선

🎉 실제 체감 개선사항

성능 향상

  • ⚡ 동시접속 처리 능력 3-5배 향상
  • 메모리 사용량 64% 감소 (186MB → 67MB)
  • ⚡ 정적 파일 처리 속도 2-3배 향상
  • 스왑 사용량 완전 제거로 예측 가능한 성능

관리 편의성

  • 🔧 설정 파일 구조화로 유지보수 용이 (웹사이트/API 분리)
  • 🔧 SSL 인증서 자동 갱신 (cron으로 완전 자동화)
  • 🔧 패키지 관리로 5분 내 업데이트 가능
  • 🔧 표준 systemctl 명령어로 관리 통일

보안 강화

  • 🔒 TLS 1.3 지원으로 최신 보안 표준 적용
  • 🔒 보안 헤더 자동 적용
  • 🔒 자동 HTTPS 리다이렉트
  • 🔒 지속적인 보안 업데이트 가능

비용 효율성

  • 💰 클라우드 메모리 비용 절약 (64% 메모리 사용량 감소)
  • 💰 관리 시간 대폭 단축 (업데이트 2시간 → 5분)
  • 💰 장애 대응 시간 감소

💡 마이그레이션 과정에서 배운 점들

1. 서버 이전은 시스템 개선의 절호의 기회

단순히 물리적 위치만 바꾸는 것이 아니라, 아키텍처 전체를 재검토할 수 있는 기회로 활용해야 한다. 우리의 경우 NHN → Naver Cloud 이전과 함께 웹서버까지 개선함으로써 일석이조의 효과를 얻었다.

2. 버전과 빌드 시기의 중요성

Apache 2.4.33 (2019년 빌드)Nginx 1.20.1 (2021년)의 차이는 단순한 숫자가 아니다. 6년간의 기술 발전과 최적화가 압축되어 있으며, 이것이 성능 차이로 직결된다.

3. 소스 컴파일 vs 패키지 관리

개발 환경에서는 소스 컴파일이 유연성을 제공하지만, 운영 환경에서는 패키지 관리가 압도적으로 유리하다. 특히 보안 패치와 업데이트 측면에서 큰 차이가 난다.

4. 서비스별 최적화의 중요성

웹사이트와 API를 같은 설정으로 처리하는 것은 비효율적이다. 각 서비스의 특성에 맞는 최적화가 전체 성능 향상의 핵심이다.

5. 모니터링과 측정의 중요성

bash

# 지속적인 모니터링 스크립트
while true; do
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    nginx_mem=$(ps aux | grep nginx | grep -v grep | awk '{sum+=$6} END {print sum/1024}')
    system_mem=$(free | grep Mem | awk '{print ($3/$2)*100}')
    echo "$timestamp: Nginx: ${nginx_mem}MB, System: ${system_mem}%" >> /var/log/performance.log
    sleep 300
done &

정확한 측정 없이는 개선도 없다. Before/After를 명확히 측정함으로써 개선 효과를 객관적으로 증명할 수 있었다.

🚀 마이그레이션 후 달라진 일상

개발팀 관점

  • 배포 시간 단축: 정적 파일 처리 최적화로 배포 후 반영 시간 감소
  • 서버 모니터링 스트레스 감소: 메모리 사용량 예측 가능해짐
  • 설정 변경 용이: 웹사이트와 API 독립적 설정 가능
  • 개발 효율성 향상: 서버 재시작 시간 단축

운영팀 관점

  • 메모리 부족 알람 사라짐: 스왑 사용량 0으로 안정성 확보
  • SSL 인증서 관리 자동화: 갱신 걱정 완전 해소
  • 장애 대응 시간 단축: 패키지 관리로 빠른 업데이트 가능
  • 성능 예측 가능: 트래픽 증가에도 안정적 성능 유지

사용자 관점

  • 페이지 로딩 속도 체감상 2-3배 향상: 특히 이미지 로딩 개선
  • API 응답 속도 정상화: 2-3초 → 200ms 이하로 복구
  • 전반적인 사용자 경험 향상: 끊김 없는 매끄러운 서비스

비즈니스 관점

  • 클라우드 비용 최적화: 메모리 효율성으로 인스턴스 비용 절약
  • 확장성 확보: 트래픽 증가에 대비한 여유 리소스 확보
  • 서비스 신뢰성 향상: 안정적인 성능으로 고객 만족도 증가

🎯 앞으로의 계획

1. 추가 최적화 검토

  • HTTP/3 (QUIC) 도입: 모바일 환경 성능 더욱 향상
  • Brotli 압축: Gzip보다 20% 더 효율적인 압축
  • CDN 연동: Naver Cloud CDN과 연계한 글로벌 서비스

2. 모니터링 시스템 고도화

bash

# Prometheus + Grafana 도입 계획
- Nginx 상태 모니터링 대시보드
- 실시간 성능 메트릭 수집
- 임계치 기반 자동 알림 시스템

3. 고가용성 구성

  • 로드 밸런서 구성: 다중 인스턴스 운영
  • 데이터베이스 분리: API 서버 독립적 확장
  • 백업 및 복구 자동화: 장애 대응 시간 최소화

4. 보안 강화

  • WAF (Web Application Firewall) 도입
  • DDoS 방어 시스템 구축
  • 로그 분석 시스템 고도화

💭 마치며

NHN에서 Naver Cloud로의 서버 이전과 함께 진행한 Apache에서 Nginx로의 마이그레이션은 단순한 웹서버 교체가 아니라, 시스템 아키텍처의 근본적인 현대화였다.

얻은 것들

  • 3배 이상의 성능 향상 (처리량, 응답시간, 메모리 효율성)
  • 64%의 메모리 절약과 스왑 사용량 완전 제거
  • 관리 복잡성 대폭 감소와 자동화된 운영 환경
  • 미래 확장성 확보와 클라우드 환경 최적화
  • 비용 효율성 개선과 운영 안정성 향상

과정에서 깨달은 것

서버 이전이라는 "어차피 해야 하는 일""기회"로 바꿔 생각한 것이 핵심이었다. 단순히 기존 시스템을 새로운 곳으로 옮기는 것이 아니라, 근본적인 문제를 해결하고 미래를 준비하는 계기로 만들었다.

비슷한 상황의 개발자들에게

만약 서버 이전이나 웹서버 교체를 고민하고 있다면:

  1. 충분한 사전 조사와 계획 - 현재 시스템의 정확한 측정부터
  2. 단계적 접근 - 한 번에 모든 것을 바꾸려 하지 말 것
  3. 모니터링 중심 사고 - 측정할 수 없으면 개선할 수 없다
  4. 사용자 관점 유지 - 기술적 개선이 실제 사용자 경험으로 이어져야 함
  5. 팀 전체의 동의 - 개발, 운영, 비즈니스 모든 측면 고려

6년 된 Apache 2.4.33에서 최신 Nginx 1.20.1로의 전환은 단순한 업그레이드가 아니라 시간을 뛰어넘는 도약이었다.

결과적으로 우리는 더 빠르고, 더 안정적이며, 더 효율적인 시스템을 얻었고, 무엇보다 미래에 대한 확신을 갖게 되었다.


기술 스택:

  • 기존: Apache 2.4.33 (Unix, 2019 빌드) + NHN 물리서버
  • 계획: Nginx 1.20.1+ + Naver Cloud Platform
  • SSL: Let's Encrypt 멀티도메인 인증서
  • 관리: 소스컴파일 → 패키지 관리 (yum)

기대 성과:

  • 메모리 사용량: 186MB → 60-80MB (60% 이상 절약 예상)
  • 스왑 사용률: 85% → 0% (완전 제거 목표)
  • 프로세스 수: 11개 → 5개 (효율성 개선)
  • 동시 처리 능력: 3-5배 향상 예상

관련 링크:

🔥 서버를 옮기기전 했던 고민

회사에서 운영하던 웹사이트 및 API 서버가 느려지기 시작했다. 비용 절약을 위해 하나의 서버에서 웹사이트와 백엔드 API를 함께 돌리고 있었는데, 트래픽이 증가하면서 문제가 표면화되었고,
이번에 서버를 옮기는 과정에 (nhn 하드웨어서버 -> navercloud) 개선을 해보도록 하였다.

문제 상황의 구체적 모습:

  • 웹사이트 응답 속도 저하: 특히 이미지와 정적 파일 로딩 지연
  • API 서버 응답 지연: JSON 응답이 평소 200ms에서 2-3초까지 늘어남
  • 서버 리소스 경합: 웹사이트 트래픽 증가 시 API 성능도 함께 저하
  • 상호 영향: API 호출이 많아지면 웹사이트도 느려지는 악순환ㄴ

메모리 사용량 모니터링으로 확인한 문제점:

Apache

RAM: 7.7G total, 5.7G used (74% 사용률)
Swap: 3.9G total, 3.3G used (85% 사용률) ← 심각한 문제

스왑 85% 사용의 의미: 시스템이 메모리 부족으로 디스크를 메모리처럼 사용하고 있다는 뜻. 디스크는 RAM보다 100-1000배 느리기 때문에 모든 작업이 극도로 느려지는 상황이었다.

🤔 왜 이번 기회에 Apache를 바꿔야 했나?

단순히 서버만 옮기면 안 되는 이유

  • 기존 문제를 그대로 새 서버로 이전하는 것
  • NHN → Naver Cloud로 옮겨도 근본적 성능 문제는 해결되지 않음
  • 오히려 클라우드 환경에서 더 큰 비용 부담이 될 수 있음

Apache 2.4.33의 한계점

bash

# 기존 Apache 서버에서 확인한 Apache 정보
$ /usr/local/apache/bin/httpd -v
Server version: Apache/2.4.33 (Unix)
Server built:   Feb 13 2019 20:00:47

문제점들:

  • 구형 버전: 6년 전 빌드로 최신 최적화 기능 부재
  • 소스 컴파일 설치: 보안 패치와 업데이트가 어려움
  • 프로세스 기반 아키텍처: 메모리 사용량이 접속자 수에 비례해서 증가
  • 정적 파일 처리 비효율: 이미지, CSS, JS도 무거운 프로세스로 처리
  • 웹사이트와 API 구분 없음: 서로 다른 특성의 서비스를 같은 방식으로 처리

구형 시스템의 관리 어려움

bash

# 보안 패치 적용 시 (소스 컴파일)
1. 새 소스코드 다운로드
2. ./configure 설정 (기존 옵션 재확인)
3. make && make install (1-2시간 소요)
4. 설정 파일 재검토 및 테스트
5. 서비스 재시작
→ 총 소요시간: 2-4시간, 장애 위험 높음

# vs 패키지 관리 시스템
yum update nginx    # 5분 완료
systemctl reload nginx

🎯 서버 이전 + 웹서버 개선 전략

새로운 아키텍처 설계

  • 인프라: NHN 물리서버 → Naver Cloud Platform
  • 웹서버: Apache 2.4.33 → Nginx 1.20.1
  • 관리 방식: 소스컴파일 → 패키지 관리
  • SSL 관리: 개별 인증서 → 통합 멀티도메인 인증서
  • 서비스 분리: 웹사이트와 API 각각 최적화된 설정

성능 목표 설정

  • 메모리 사용량 50% 이상 감소
  • 스왑 사용량 0으로 만들기
  • API 응답 시간 200ms 이하로 복구
  • 정적 파일 로딩 속도 2배 이상 향상
  • 관리 복잡성 대폭 감소

🚀 왜 Nginx였나?

Apache vs Nginx - 우리 상황에서의 비교

Apache의 한계 (우리가 겪은 문제):

  • 메모리 사용량: 요청당 프로세스/스레드 생성으로 메모리 사용량 높음
  • 동시 접속 처리: prefork 모델의 한계로 대량 접속 시 성능 저하
  • 정적 파일 처리: 모든 요청을 동일하게 처리하여 비효율적
  • 리소스 경합: 웹사이트와 API가 같은 프로세스 풀 공유

Nginx가 우리 상황에 완벽했던 이유:

  • 이벤트 기반 아키텍처: 웹사이트의 많은 동시 요청과 API의 빠른 응답 처리 모두 효율적
  • 뛰어난 정적 파일 처리: 웹사이트 이미지, CSS, JS를 직접 빠르게 서빙
  • 효율적인 리버스 프록시: API 서버를 위한 백엔드 Tomcat 최적화
  • 독립적 설정: 웹사이트용과 API용 각각 다른 최적화 설정 적용 가능
  • 클라우드 환경 최적화: 가변적 트래픽에 효율적 대응, 비용 절약 효과
  • 현대적 웹 지원: HTTP/2, SSL 최적화, 압축 등 최신 기술 지원

결정적으로, "웹사이트는 웹사이트답게, API는 API답게" 각각의 특성에 맞춰 최적화할 수 있다는 점이 가장 매력적이었다.

다음으로 실전 마이그레이션 과정은 다음 포스트로 정리하도록 하겠다 .

플러터 앱 출시를 위해선 앱 아이콘을 변경을 해주어야한다. 아니면 기본 flutter 아이콘으로 앱이 나오기 때문이다.

이럴 때 유용하게 사용할 수 있는 라이브러리가 flutter_launcher_icons 이다!

 

https://pub.dev/packages/flutter_launcher_icons

 

flutter_launcher_icons | Dart package

A package which simplifies the task of updating your Flutter app's launcher icon.

pub.dev

 

1. 설치

터미널에 해당 명령어를 입력핵서 설치한다

2. 설정

1) pubspec.yaml 설정

Flutter 프로젝트의 pubspec.yaml에 아래와 같이 설정합니다:

dev_dependencies:
  flutter_launcher_icons: ^0.13.1

flutter_icons:
  android: true
  ios: true
  image_path: "assets/app_logo.png" # 아이콘 경로

3. 설정 업데이트

아이콘 파일을 생성하고 설정을 업데이트하기 위해 아래 명령어를 실행합니다:
명령어를 다로 실행 안하면 해당 로고 이미지들이 설정이 안됨

업데이트하기 위해 아래 명령어를 실행합니다:

flutter pub run flutter_launcher_icons

4. flutter_launcher_icons 명령어의 결과

Android

  1. 아이콘 생성:
    • android/app/src/main/res/mipmap- 디렉토리 아래에 다양한 해상도의 아이콘 생성.
  2. 설정 업데이트:
    • AndroidManifest.xmlandroid:iconandroid:roundIcon이 업데이트됨:
    • <application android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round">

iOS

  1. 아이콘 생성:
    • ios/Runner/Assets.xcassets/AppIcon.appiconset 디렉토리에 아이콘 생성.
  2. 설정 업데이트:
    • iOS 설정이 자동으로 업데이트되어 앱 아이콘이 변경.

┌─ Flutter Fix ────────────────────────────────────────────────────────────────────────────────────┐ │ [!] This is likely due to a known bug in Android Gradle Plugin (AGP) versions less than 8.2.1, │ │ when │ │ 1. setting a value for SourceCompatibility and │ │ 2. using Java 21 or above. │ │ To fix this error, please upgrade your AGP version to at least 8.2.1. The version of AGP that │ │ your project uses is likely defined in: │ ~~ 블라블라

 

-> 해당 오류코드가 발생할 때가 있다, (Flutter 최신버전으로 create 했어도 AGP 버전이 낮은 경우가 항상 있다...)
이럴땐 AGP 버전을 업그레이드 해야 하는데, (android/build.gradle 또는 settings.gradle 파일에서 수정해도 되는데,

아래 방법을 사용하면 간편하게 수정을 할 수 있다.

해당 기록을 남기기 위해 글을 써본다.

이 폴더 부분만 안드로이드 스튜디오로 따로 실행

해당 부분을 안드로이드 스튜디오로 따로 실행하면

아래 Assistant 가 뜨고 버전을 업그레이드 할 수 있게 나온다
그 후 위에 나온 파란 버튼인 Run selected steps 를 누르고 나오는 스탭대로 과정을 진행하면 자동으로 AGP 가 업그레이드 되고 
해당 오류는 발생하지 않게 된다.

Java Supplier와 Lazy Evaluation: 성능 최적화를 위한 사용법

Java에서 Supplier는 매개변수를 받지 않고 값을 반환하는 함수형 인터페이스입니다. 이는 주로 필요할 때 값을 생성하거나 계산을 지연시키기 위한 목적으로 사용됩니다. 이번 글에서는 Supplier를 활용한 Lazy Evaluation(지연 계산)의 개념과 사용법, 그리고 이를 통해 얻을 수 있는 성능 최적화를 소개하겠습니다.


Supplier란?

Supplier는 Java의 함수형 인터페이스 중 하나로, get 메서드를 통해 값을 반환합니다. 다른 함수형 인터페이스와 달리 매개변수를 받지 않고 값을 반환한다는 특징이 있습니다.

예제 코드:

import java.util.function.Supplier;

public class SupplierExample {
    public static void main(String[] args) {
        Supplier<String> helloSupplier = () -> "Hello, World!";

        String result = helloSupplier.get(); // get 메서드를 호출하여 값 반환
        System.out.println(result); // 출력: Hello, World!
    }
}

Supplier를 사용하는 이유

단순히 값을 반환한다면 별도의 함수나 변수로 처리할 수도 있습니다. 그렇다면 왜 굳이 Supplier를 사용할까요?

핵심 이유: 실행 시점을 명시적으로 제어할 수 있기 때문입니다. 이를 통해 불필요한 연산을 피하고 성능을 최적화할 수 있습니다.

이를 Lazy Evaluation(지연 계산)이라고 부릅니다.

Lazy Evaluation의 효과

Supplier는 값을 필요할 때만 생성하도록 함으로써 불필요한 연산을 방지합니다.

예를 들어, 값이 반드시 필요하지 않은 상황에서 값 생성을 미리 수행하면 성능 저하가 발생할 수 있습니다. 이를 방지하기 위해 Supplier로 값을 필요할 때만 생성하도록 제어할 수 있습니다.


Lazy Evaluation 예제: Supplier 사용 전과 후

1. Supplier 없이 구현한 경우:

import java.util.concurrent.TimeUnit;

public class NoSupplier {
    public static void main(String[] args) throws InterruptedException {
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 5; i++) {
            printRandom(getRandom(), i);
        }

        System.out.println((System.currentTimeMillis() - startTime) / 1000 + " seconds");
    }

    public static double getRandom() throws InterruptedException {
        TimeUnit.SECONDS.sleep(3); // 3초 대기
        return Math.random();
    }

    public static void printRandom(double value, int count) {
        if (count % 2 == 0) {
            System.out.println(value);
        }
    }
}

실행 결과:

  • getRandom 함수는 5번 모두 호출됩니다.
  • printRandom 메서드는 짝수일 때만 값을 출력하지만, 값 생성(getRandom)은 모든 루프에서 실행됩니다.
  • 총 소요 시간은 3초 x 5 = 15초.

문제점:

  • 짝수(i = 0, 2, 4)일 때만 결과가 필요하지만, 모든 경우에 값이 미리 계산됩니다.

2. Supplier를 사용한 경우:

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

public class SupplierExample {
    public static void main(String[] args) throws InterruptedException {
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 5; i++) {
            printRandom(() -> getRandom(), i); // Supplier 전달
        }

        System.out.println((System.currentTimeMillis() - startTime) / 1000 + " seconds");
    }

    public static double getRandom() throws InterruptedException {
        TimeUnit.SECONDS.sleep(3); // 3초 대기
        return Math.random();
    }

    public static void printRandom(Supplier<Double> supplier, int count) {
        if (count % 2 == 0) {
            System.out.println(supplier.get()); // 필요할 때만 호출
        }
    }
}

실행 결과:

  • supplier.get()이 호출되는 경우에만 getRandom 함수가 실행됩니다.
  • 호출된 횟수는 짝수(i = 0, 2, 4)일 때만 3번 호출됩니다.
  • 총 소요 시간은 3초 x 3 = 9초.

개선점:

  • 불필요한 연산 제거: 조건이 만족될 때만 값 생성.
  • 성능 최적화: 연산 시간이 15초에서 9초로 줄어듦.

Supplier의 응용

Supplier는 단순한 값 반환뿐만 아니라 복잡한 연산이 포함된 값을 지연 평가(Lazy Evaluation)로 처리할 때 매우 유용합니다.

응용 사례:

  1. Lazy Loading: 필요한 시점에만 데이터나 객체를 로드.
  2. 캐싱: 값을 미리 생성하지 않고 요청 시에만 계산.
  3. 조건부 실행: 특정 조건을 만족할 때만 값을 생성.

결론

Supplier는 Java에서 지연 계산(Lazy Evaluation)을 가능하게 하는 강력한 도구입니다.

  • 불필요한 연산을 줄이고 성능을 최적화할 수 있습니다.
  • 코드의 실행 흐름을 명시적으로 제어할 수 있습니다.

1. 필수 준비물

  • 맥북의 경우 C to C 케이블 필수

2. Android 기기에서 개발자 모드 활성화

Flutter 앱을 실제 Android 기기에 배포하기 위해서는 개발자 모드를 활성화하고, USB 디버깅을 허용해야 합니다.

  1. 개발자 모드 활성화:
    • 설정 > 휴대전화 정보 또는 디바이스 정보로 이동합니다.
    • 휴대전화 정보 에서 소트웨어 정보에 들어갑니다
    • 빌드 번호를 7번 연속으로 탭합니다.
    • "개발자 모드가 활성화되었습니다"라는 메시지가 나타납니다.
  2. USB 디버깅 활성화:

이 과정을 통해 Android 기기가 디버깅 모드에서 컴퓨터와 통신할 수 있게 됩니다.


3. Android 기기를 컴퓨터에 연결

  1. USB 케이블로 기기 연결:
    • 준비한 USB 케이블을 사용해 Android 기기와 컴퓨터를 연결합니다.
    • 기기를 연결하면 Android 기기 화면에 "이 컴퓨터에서 USB 디버깅을 허용하겠습니까?"는 팝업이 표시됩니다. 허용을 선택하세요.
    • 핸드폰 및 컴퓨터(맥북) 두가지 허용 여부가 나오는데 둘 다 허용을 눌러야합니다


4. Flutter 디바이스 연결 상태 확인

Flutter 개발 환경에서 기기가 정상적으로 연결되었는지 확인해야 합니다.

  1. 터미널 또는 명령 프롬프트에서 아래 명령어를 실행하여 연결된 Android 기기를 확인합니다:
  2. flutter devices
  3. 결과가 출력되면, 연결된 기기의 모델명과 ID가 표시되어야 합니다. 예시 출력:만약 기기가 보이지 않으면, USB 디버깅 설정을 다시 확인하고 케이블을 재연결해보세요.
  4. 2 connected devices: SM-N960N (mobile) • 1234567890abcdef • android-arm64 • Android 10 (API 29)

5. Flutter 앱을 Android 기기에 배포

  1. ide 를 사용하는법

    VS CODE 나 Android Studio 에서 위에 디바이스 부분에 연결한 기기가 자동으로 설정되어 있는데

그대로 실행하면 된다.(캡쳐 사진은 연결 안 되어있는 상태 연결되면 해당 기기 이름이 뜨게 되어있다.)

  1. 명령어를 사용해서 실행하는법
    Flutter 앱을 실제 기기에서 실행하려면 다음 명령어를 사용합니다.
    1. 터미널에서 Flutter 프로젝트 디렉토리로 이동한 후, 아래 명령어를 실행합니다:
    flutter run
    1. 앱이 빌드되고 연결된 Android 기기에 설치됩니다. 설치가 완료되면 앱이 자동으로 실행되며, 터미널에서는 앱의 로그와 상태를 실시간으로 확인할 수 있습니다.

6. 앱 로그 확인 및 디버깅

  1. 앱이 실행되면, 터미널에서 앱의 실시간 로그를 확인할 수 있습니다. 로그를 통해 앱이 어떻게 실행되고 있는지, 에러가 발생하는지 확인할 수 있습니다.
  2. 실시간 디버깅을 위해 앱 실행 중에 코드를 수정한 후 저장하면, Flutter의 Hot Reload 기능을 통해 빠르게 변경된 내용을 반영할 수 있습니다.

7. 릴리즈 APK 빌드하여 배포

테스트를 완료한 후, 다른 사람에게 앱을 설치할 수 있도록 릴리즈 APK를 빌드할 수 있습니다. 릴리즈 모드에서는 앱이 최적화되어 빌드됩니다. ()

  1. 릴리즈 모드 APK를 빌드하려면 아래 명령어를 실행합니다:
  2. flutter build apk --release
  3. 빌드가 완료되면, build/app/outputs/flutter-apk/app-release.apk 파일이 생성됩니다. 이 APK 파일을 다른 기기에서 설치하고 실행할 수 있습니다.

알고리즘 공부시에
C++에서 자주 사용하는 <bits/stdc++.h> 헤더 파일을 Mac에서 기본적으로 사용할 수는 없어서 따로 설정을 통하여 추가를 합니다.

  • 참고로 <bits/stdc++.h> 헤더파일은 모든 표준 라이브러리를 한 번에 포함하는 파일
  • 자바와 다르게 C++은 컴파일러가 필요한 헤더 파일을 자동으로 찾지 않는다.
  • 또한 어느 라이브러리에서 제공되는지를 명확하게 표시해야 한다.

따로 추가를 하지 않으면 해당 단어를 적었을 때 빨간줄이 뜰 것입니다.

 

1. clang 버전 확인하기

Mac에서는 Xcode가 clang을 기본 C++ 컴파일러로 사용합니다. 특정 기능이나 헤더 파일은 clang의 버전과 호환성 문제를 일으킬 수 있으므로, 설치된 clang의 버전을 확인하는 것이 유용합니다.

(다른 블로그들에는 특정 경로로만 들어가서 설치하라고 하는데 해당 경로로 들어갔을때는 개인적으로 제대로 되질 않았습니다. 직접 각각의 경로를 확인해서 설치하는게 맞다고 생각합니다.)

clang --version
예시 출력:
Apple clang version 16.0.0 (clang-1600.0.26.3)
Target: arm64-apple-darwin23.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

위와 같은 결과가 나오면, 설치된 clang 버전이 표시됩니다. 이제 다음 단계로 넘어가겠습니다.


2. bits 폴더 만들기

Mac에서는 기본적으로 <bits/stdc++.h> 파일이 제공되지 않기 때문에 직접 설정해야 합니다. 이를 위해 Xcode 툴체인에 bits라는 폴더를 만들어야 합니다.

먼저, Xcode 툴체인 디렉토리로 이동합니다. 터미널에 다음 명령어를 입력하세요: (위에 나온 InstalledDir 부분 을 cd 뒤에 붙여넣는다. 사람마다 다르다)

cd /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include

include 폴더로 이동한 후, bits라는 폴더를 생성합니다:
(개인적으로 mkdir: bits Permission denied가 발생해서 bits 라는 폴더를 만들때 sudo 로 권한을 주었다.)

sudo mkdir bits

sudo 명령어를 사용하기 때문에 관리자 비밀번호를 묻는 메시지가 나올 수 있습니다. 터미널에 비밀번호를 입력하면 폴더가 생성됩니다.

예시 출력:
Password:

비밀번호를 입력한 후, 오류가 없으면 bits 폴더가 성공적으로 생성됩니다.


3. stdc++.h 파일 생성하기

이제 새로 생성한 bits 폴더에 <bits/stdc++.h> 파일을 만들어줍니다. 터미널에 다음 명령어를 입력하세요:

cd bits
sudo touch stdc++.h

이 명령어는 빈 stdc++.h 파일을 생성하는 명령입니다. 이제 bits 폴더 안에 이 파일이 생성되었습니다.


4. GitHub에서 코드 복사

이제 <bits/stdc++.h> 파일에 들어갈 내용을 복사해야 합니다. 다음 링크로 이동하여 stdc++.h 파일의 내용을 복사하세요:

GitHub 링크: bits/stdc++.h for Mac

  1. 링크에 접속한 후, 페이지의 모든 내용을 드래그하여 복사합니다.
    • Command + C를 눌러 내용을 복사합니다.
  2. 터미널로 돌아와서, 편집 모드 상태인 vi 에디터에 복사한 내용을 붙여넣습니다.
    • 붙여넣는 방법: Command + V 또는 Shift + Insert (터미널 설정에 따라 다를 수 있습니다)
  3. 개인적으로 복사한 부분

  // C
  #ifndef _GLIBCXX_NO_ASSERT
  #include <cassert>
  #endif
  #include <cctype>
  #include <cerrno>
  #include <cfloat>
  #include <ciso646>
  #include <climits>
  #include <clocale>
  #include <cmath>
  #include <csetjmp>
  #include <csignal>
  #include <cstdarg>
  #include <cstddef>
  #include <cstdio>
  #include <cstdlib>
  #include <cstring>
  #include <ctime>

  #if __cplusplus >= 201103L
  #include <ccomplex>
  #include <cfenv>
  #include <cinttypes>
  #include <cstdbool>
  #include <cstdint>
  #include <ctgmath>
  #include <cwchar>
  #include <cwctype>
  #include <exception>
  #include <stdexcept>
  #endif

  // C++
  #include <algorithm>
  #include <bitset>
  #include <complex>
  #include <deque>
  #include <exception>
  #include <fstream>
  #include <functional>
  #include <iomanip>
  #include <ios>
  #include <iosfwd>
  #include <iostream>
  #include <istream>
  #include <iterator>
  #include <limits>
  #include <list>
  #include <locale>
  #include <map>
  #include <memory>
  #include <new>
  #include <numeric>
  #include <ostream>
  #include <queue>
  #include <set>
  #include <sstream>
  #include <stack>
  #include <stdexcept>
  #include <streambuf>
  #include <string>
  #include <typeinfo>
  #include <utility>
  #include <valarray>
  #include <vector>

  #if __cplusplus >= 201103L
  #include <array>
  #include <atomic>
  #include <chrono>
  #include <condition_variable>
  #include <forward_list>
  #include <future>
  #include <initializer_list>
  #include <mutex>
  #include <random>
  #include <ratio>
  #include <regex>
  #include <scoped_allocator>
  #include <system_error>
  #include <thread>
  #include <tuple>
  #include <typeindex>
  #include <type_traits>
  #include <unordered_map>
  #include <unordered_set>
  #endif

5. vi 에디터에서 파일 열기

아까 계속 실행하던 터미널 디렉토리 화면으로 다시 돌아와 해당 부분을 vi 에디터로 연다.

sudo vi stdc++.h

이 명령어를 입력하면 vi 에디터가 열립니다. 처음에는 편집 모드가 아니므로 파일을 수정할 수 없습니다. 수정하려면 먼저 편집 모드로 들어가야 합니다.

편집 모드로 진입하려면 i 키를 누릅니다. (a도 가능)


6. 파일 저장 및 종료

복사한 내용을 vi 에디터에 붙여넣었다면(커멘드 + v) 이제 파일을 저장하고 종료할 차례입니다.

  1. vi 편집 모드에서 벗어나려면 ESC 키를 누릅니다.
  2. 그런 다음 :wq를 입력하고 Enter 키를 누릅니다.
:wq

이 명령은 파일을 저장(w)하고 vi 에디터를 종료(q)하는 명령입니다. (그냥 저장 안하고 나가려면 q! 를 대신 입력해라)

예시 출력:
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/bits/stdc++.h" 123L, 4567C written

이 메시지는 파일이 성공적으로 저장되었음을 나타냅니다.


7. 확인하기

이제 <bits/stdc++.h> 헤더 파일을 사용할 준비가 되었습니다. C++ 코드에서 다음과 같이 테스트해볼 수 있습니다:

#include <bits/stdc++.h>
using namespace std;

int main() {
    cout << "Hello, World!" << endl;
    return 0;
}

clang++로 코드를 컴파일하여 오류가 없으면 성공적으로 설정된 것입니다.

 

8. 지금까지의 전체코드를 표현한 스크린샷

vi 화면 들어간 부분빼고는 실제로 이런 코드로 움직였습니다!.