<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>DevNote</title>
<link>https://note.chanyeongpark.com</link>
<description>Chanyeong Park's DevNote</description>
<language>ko</language>
<pubDate>Wed, 21 Jan 2026 23:39:36 +0900</pubDate>
<generator>DevNote</generator>
<ttl>100</ttl>
<managingEditor>DevNote</managingEditor>
<item>
<title>[Javascript] scroll 내리면 header 숨기기 &amp; 올리면 header 보이기</title>
<link>https://note.chanyeongpark.com/posts/jquery/35</link>
<description>스크롤을 아래로 내리면 header가 위로 올라가 숨겨지고,반대로 올리면 다시 등장하는 효과CSS#header{position: fixed;top: 0;left: 0;width: 100%;z-index: 90;background: #fff;padding: 0 20px;box-sizing: border-box;}#header.hidden{top: -120px;transition: all 0.4s;}Javascript// header actionconst header = document.querySelector(&amp;#39;#header&amp;#39;);if (!header) return;let lastY = window.scrollY;const threshold = 10; // 미세 스크롤 흔들림 방지const onScroll = () =&amp;gt; {  const y = window.scrollY;  // 최상단이면 항상 노출  if (y &amp;lt;= 0) {  header.classList.remove(&amp;#39;hidden&amp;#39;);  lastY = y;  return;  }  // 아래로 스크롤하면 숨김  if (y &amp;gt; (lastY + threshold) + 100) {  header.classList.add(&amp;#39;hidden&amp;#39;);  lastY = y;  return;  }  // 위로 스크롤하면 노출  if (y &amp;lt; lastY - threshold) {  header.classList.remove(&amp;#39;hidden&amp;#39;);  lastY = y;  }};window.addEventListener(&amp;#39;scroll&amp;#39;, onScroll, { passive: true });</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/35</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/35</comments>
<pubDate>Wed, 21 Jan 2026 23:39:36 +0900</pubDate>
</item>
<item>
<title>[GSAP] ScrollSmoother 사용시 #해시 클릭하면 해당 id의 엘리먼트로 scrollTo 처리</title>
<link>https://note.chanyeongpark.com/posts/jquery/34</link>
<description>GSAP에서 ScrollSmoother 사용시 부드러운 스크롤링이 작동하는데,a 태그의 #링크 클릭시 해당 id의 엘리먼트로 스크롤이 이동해야 하는 상황.또는, domain.com/page.html#sec1 과 같이 주소로 접속한 경우에도 해당 id의 엘리먼트로 스크롤이 이동해야 하는 상황.일반적인 방법으로 offset.top 만큼 scorllTo 하게 되면 ScrollSmoother 가 위치를 잡지 못하므로아래 코드를 활용하면 된다.HTML 코드&amp;lt;ul class=&amp;quot;product_tab2&amp;quot;&amp;gt;    &amp;lt;li class=&amp;quot;active&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#sec1&amp;quot;&amp;gt;LG U+ LTE&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;    &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#sec2&amp;quot;&amp;gt;LG U+ 5G&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;    &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#sec3&amp;quot;&amp;gt;SKT&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;    &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#sec4&amp;quot;&amp;gt;KT&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;    &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#sec5&amp;quot;&amp;gt;모바일라우터&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;Javascript(GSAP) 코드// # 클릭시 해당영역 이동(() =&amp;gt; { const links = document.querySelectorAll(&amp;#39;.product_nav a, .product_tab2 a&amp;#39;); const OFFSET = 100; // 위가 잘리면 늘리고(80~140), 너무 뜨면 줄이세요. links.forEach(a =&amp;gt; {  a.addEventListener(&amp;#39;click&amp;#39;, (e) =&amp;gt; {   const hash = a.getAttribute(&amp;#39;href&amp;#39;);   if (!hash || !hash.startsWith(&amp;#39;#&amp;#39;)) return;   const target = document.querySelector(hash);   if (!target) return;   e.preventDefault();   const smoother = (window.ScrollSmoother &amp;amp;&amp;amp; ScrollSmoother.get) ? ScrollSmoother.get() : null;   if (smoother) {    smoother.scrollTo(target, true, `top-=${OFFSET}`);   } else {    // smoother 없는 모바일/비PC 대응    const y = target.getBoundingClientRect().top + window.pageYOffset - OFFSET;    window.scrollTo({ top: y, behavior: &amp;#39;smooth&amp;#39; });   }   history.pushState(null, &amp;#39;&amp;#39;, hash);  }); });})();// # 포함된 주소로 접속하는 경우 해당 섹션으로 이동window.addEventListener(&amp;#39;load&amp;#39;, () =&amp;gt; { const hash = location.hash; if (!hash) return; const target = document.querySelector(hash); if (!target) return; const OFFSET = 100; // 위가 잘리면 조절 (보통 80~120) // 브라우저 기본 점프 리셋 window.scrollTo(0, 0); // ScrollSmoother가 있는 경우(PC) const smoother = (window.ScrollSmoother &amp;amp;&amp;amp; ScrollSmoother.get)  ? ScrollSmoother.get()  : null; if (smoother) {  // GSAP 레이아웃 계산 완료 후 이동  gsap.delayedCall(0.15, () =&amp;gt; {   smoother.scrollTo(target, true, `top-=${OFFSET}`);  }); } else {  // 모바일 / smoother 없는 환경  const y = target.getBoundingClientRect().top + window.pageYOffset - OFFSET;  window.scrollTo({ top: y, behavior: &amp;#39;auto&amp;#39; }); }});</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/34</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/34</comments>
<pubDate>Wed, 21 Jan 2026 17:07:34 +0900</pubDate>
</item>
<item>
<title>Rocky Linux 에서 현재 웹포트(80,443) 에 접속해있는 아이피 상위 N개 출력하기</title>
<link>https://note.chanyeongpark.com/posts/linux/33</link>
<description>현재 서버의 웹포드(80, 443)에 접속해 있는 클라이언트 IP를 상위 N개를 출력하는 셸 스크립트.서버에 DDOS가 들어온 것으로 추측되는 경우 빠르게 스캔하여 접속량이 많은 상위 IP를 차단하는데 도움을 줄 수 있다.셸 스크립트 파일 작성아래 내용으로 스크립트 파일을 작성한다.# vi ~/2.top_clients_80_443.sh파일을 먼저 만든 후 아래 내용을 복사 붙여넣기!#!/usr/bin/env bash# 80/443로 열린 TCP 연결의 클라이언트 IP 상위 N개# - 상태 필터 X (ss 구문 오류 회피), LISTEN만 제외# - LocalAddress:Port가 :80 또는 :443으로 끝나는 것만 카운트# - IPv4/IPv6/스코프 처리set -euo pipefailLIMIT=&amp;quot;${LIMIT:-50}&amp;quot;             # 상위 N개PORTS_REGEX=&amp;quot;${PORTS_REGEX:-:(80|443)$}&amp;quot;   # 로컬 포트 필터SS=&amp;quot;${SS:-ss}&amp;quot;                # ss 경로 커스텀 가능(예: /usr/sbin/ss)command -v &amp;quot;$SS&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || { echo &amp;quot;ss 명령(iproute2)이 필요합니다.&amp;quot;; exit 1; }# -a: 모든 소켓(리슨 포함), -H: 헤더 제거, -n: 숫자# awk에서 1열(state)이 LISTEN인 건 제외&amp;quot;$SS&amp;quot; -a -H -n -t \| awk -v rp=&amp;quot;$PORTS_REGEX&amp;quot; &amp;#39; $1 == &amp;quot;LISTEN&amp;quot; { next }    # LISTEN 제외 {  # 일반 형식: State Recv-Q Send-Q LocalAddress:Port PeerAddress:Port  local=$4; peer=$5  # 로컬 포트가 80/443 아니면 제외  if (local !~ rp) next  # peer 정리: users:(...) 꼬리 제거  sub(/,.*$/, &amp;quot;&amp;quot;, peer)  # IPv6 대괄호 제거  gsub(/^\[/, &amp;quot;&amp;quot;, peer); gsub(/\]$/, &amp;quot;&amp;quot;, peer)  # 인터페이스 스코프(%eth0 등) 제거  sub(/%.+$/, &amp;quot;&amp;quot;, peer)  # 끝의 :포트 제거  sub(/:[0-9]+$/, &amp;quot;&amp;quot;, peer)  # 로컬루프백 제외(원하면 주석)  if (peer == &amp;quot;127.0.0.1&amp;quot; || peer == &amp;quot;::1&amp;quot;) next  cnt[peer]++ } END {  for (ip in cnt) printf &amp;quot;%7d %s\n&amp;quot;, cnt[ip], ip }&amp;#39; \| sort -nr \| head -n &amp;quot;$LIMIT&amp;quot;결과 화면셸 스크립트를 실행하면 아래와 같이 요약 정보를 보여준다.# ~/2.top_clients_80_443.sh</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/33</guid>
<comments>https://note.chanyeongpark.com/posts/linux/33</comments>
<pubDate>Wed, 12 Nov 2025 00:03:42 +0900</pubDate>
</item>
<item>
<title>Rocky Linux 에서 현재 서버 상태를 보여주는 스크립트 파일 만들기</title>
<link>https://note.chanyeongpark.com/posts/linux/32</link>
<description>현재 서버의 cpu, ram, 네트워크, 저장공간 사용량, 포트별 접속자수를 간략하게 정리하여 화면에 한번에 보여주는 셸 스크립트.sh 작성은 로키리눅스 8.10 에서 진행했다.셸 스크립트 파일 작성아래 내용으로 스크립트 파일을 작성한다.# vi ~/1.server_health.sh파일을 먼저 만든 후 아래 내용을 복사 붙여넣기!#!/usr/bin/env bash# Rocky Linux 8.x &amp;mdash; 웹 느려짐 대응 원샷 점검 스크립트 (최종)# - CPU/메모리 사용률# - 디스크 사용량# - NIC별 네트워크 총량 (MB/s) &amp;mdash; 기본 enp3s0, IFACE=... 로 변경 가능# - 포트별 접속자 수 (80, 443, 40021) &amp;mdash; ss 기반, nftables 불필요set -euo pipefail# ===== 설정 =====IFACE=&amp;quot;${IFACE:-enp3s0}&amp;quot;   # 기본 NIC 고정(환경변수로 덮어쓰기 가능: IFACE=bond0 ./server_health_final.sh)PORTS=(80 443 40021)     # 모니터링 포트SAMPLE=&amp;quot;${SAMPLE:-1}&amp;quot;     # 샘플링 간격(초)bold(){ printf &amp;quot;\e[1m%s\e[0m\n&amp;quot; &amp;quot;$*&amp;quot;; }hr(){ printf -- &amp;quot;----------------------------------------------\n&amp;quot;; }# ===== CPU / MEM =====cpu_sample(){ # /proc/stat 델타로 CPU 사용률 계산 read -r t1 i1 &amp;lt; &amp;lt;(awk &amp;#39;/^cpu /{for(i=2;i&amp;lt;=NF;i++) s+=$i; print s,$5}&amp;#39; /proc/stat) sleep &amp;quot;$SAMPLE&amp;quot; read -r t2 i2 &amp;lt; &amp;lt;(awk &amp;#39;/^cpu /{for(i=2;i&amp;lt;=NF;i++) s+=$i; print s,$5}&amp;#39; /proc/stat) local dt=$((t2 - t1)) di=$((i2 - i1)) awk -v dt=&amp;quot;$dt&amp;quot; -v di=&amp;quot;$di&amp;quot; &amp;#39;BEGIN{ if(dt&amp;gt;0) printf &amp;quot;%.1f&amp;quot;, (1 - di/dt)*100; else print &amp;quot;0.0&amp;quot; }&amp;#39;}mem_usage(){ # MemAvailable 기준 사용률 awk &amp;#39;  /^MemTotal:/ {t=$2}  /^MemAvailable:/ {a=$2}  END{   used=t-a; pct=(used/t)*100;   printf &amp;quot;%.1f (%d/%d MiB)&amp;quot;, pct, used/1024, t/1024  }&amp;#39; /proc/meminfo}# ===== 디스크 =====disk_usage(){ df -h -x tmpfs -x devtmpfs --output=source,size,used,avail,pcent,target | sed 1d}# ===== 네트워크(지정 NIC만) =====net_sample_sysfs(){ local nic=&amp;quot;$1&amp;quot; local rxp=&amp;quot;/sys/class/net/$nic/statistics/rx_bytes&amp;quot; local txp=&amp;quot;/sys/class/net/$nic/statistics/tx_bytes&amp;quot; if [[ ! -r &amp;quot;$rxp&amp;quot; || ! -r &amp;quot;$txp&amp;quot; ]]; then  printf &amp;quot; - %s 통계 파일 접근 불가 (%s / %s)\n&amp;quot; &amp;quot;$nic&amp;quot; &amp;quot;$rxp&amp;quot; &amp;quot;$txp&amp;quot;  return 1 fi local rx1 tx1 rx2 tx2 read -r rx1 &amp;lt;&amp;quot;$rxp&amp;quot; read -r tx1 &amp;lt;&amp;quot;$txp&amp;quot; sleep &amp;quot;$SAMPLE&amp;quot; read -r rx2 &amp;lt;&amp;quot;$rxp&amp;quot; read -r tx2 &amp;lt;&amp;quot;$txp&amp;quot; local dr=$((rx2 - rx1)) dt=$((tx2 - tx1)) awk -v n=&amp;quot;$nic&amp;quot; -v dr=&amp;quot;$dr&amp;quot; -v dt=&amp;quot;$dt&amp;quot; -v s=&amp;quot;$SAMPLE&amp;quot; &amp;#39;BEGIN{  rin=(dr/1048576)/s; rout=(dt/1048576)/s;  if(rin&amp;lt;0)rin=0; if(rout&amp;lt;0)rout=0;  printf &amp;quot; - %s IN: %.2f MB/s | OUT: %.2f MB/s\n&amp;quot;, n, rin, rout }&amp;#39;}# ===== 포트별 접속자 수(ss) =====per_port_conns(){ # established 기준 inbound/outbound 연결 수 for p in &amp;quot;${PORTS[@]}&amp;quot;; do  # inbound: dport=포트로 들어오는 연결  local c_in c_out  c_in=$(ss -nt state established &amp;quot;( dport = :$p )&amp;quot; 2&amp;gt;/dev/null | tail -n +2 | wc -l || echo 0)  # outbound: sport=포트에서 나가는 연결  c_out=$(ss -nt state established &amp;quot;( sport = :$p )&amp;quot; 2&amp;gt;/dev/null | tail -n +2 | wc -l || echo 0)  printf &amp;quot; - port %s: IN %s conns | OUT %s conns\n&amp;quot; &amp;quot;$p&amp;quot; &amp;quot;$c_in&amp;quot; &amp;quot;$c_out&amp;quot; done}# ===== 메인 =====main(){ bold &amp;quot;[ Server Health Snapshot &amp;mdash; $(date &amp;#39;+%F %T&amp;#39;) ]&amp;quot; hr printf &amp;quot;CPU Usage: %s%%\n&amp;quot; &amp;quot;$(cpu_sample)&amp;quot; printf &amp;quot;Mem Usage: %s\n&amp;quot;  &amp;quot;$(mem_usage)&amp;quot; hr bold &amp;quot;Disk Usage:&amp;quot; disk_usage hr bold &amp;quot;Network (MB/s by NIC &amp;mdash; fixed): %s&amp;quot; &amp;quot;$IFACE&amp;quot; net_sample_sysfs &amp;quot;$IFACE&amp;quot; || true hr bold &amp;quot;Per-Port (connections)&amp;quot; per_port_conns hr}main결과 화면셸 스크립트를 실행하면 아래와 같이 요약 정보를 보여준다.# ~/1.server_health.sh</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/32</guid>
<comments>https://note.chanyeongpark.com/posts/linux/32</comments>
<pubDate>Tue, 11 Nov 2025 23:55:40 +0900</pubDate>
</item>
<item>
<title>[GSAP] fullpage 대체 효과 구현</title>
<link>https://note.chanyeongpark.com/posts/jquery/32</link>
<description>바닐라스크립트 + GSAP 환경에서 jquery 의 fullpage 효과를 대체하는 효과.GSAP 의 ScrollToPlugin플러그인을 활용해 구현 가능하다.코드가 제공하는 기능은 아래와 같다.- 마우스 스크롤시 섹션 자동 이동- 모바일에서는 fullpage 효과 해제 (리사이징시에도 적용)- 섹션 이동시 주소표시줄에 해시(#) 적용- 해시로 접속한 경우 해당 섹션 자동으로 이동- navigation 적용HTML 코드&amp;lt;!-- 컨텐츠 섹션 --&amp;gt;&amp;lt;div id=&amp;quot;content&amp;quot;&amp;gt;  &amp;lt;div class=&amp;quot;panel&amp;quot;&amp;gt;첫번째 섹션&amp;lt;/div&amp;gt;  &amp;lt;div class=&amp;quot;panel&amp;quot;&amp;gt;두번째 섹션&amp;lt;/div&amp;gt;  &amp;lt;div class=&amp;quot;panel&amp;quot;&amp;gt;세번째 섹션&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;!-- navigation --&amp;gt;&amp;lt;div id=&amp;quot;pagination&amp;quot;&amp;gt;  &amp;lt;ul&amp;gt;    &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot;&amp;gt;&amp;lt;p&amp;gt;첫번째 섹션으로&amp;lt;/p&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;    &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot;&amp;gt;&amp;lt;p&amp;gt;두번째 섹션으로&amp;lt;/p&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;    &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot;&amp;gt;&amp;lt;p&amp;gt;세번째 섹션으로&amp;lt;/p&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;  &amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;Javascript(GSAP) 코드////////////////////// fullpage 대체 효과////////////////////gsap.registerPlugin(ScrollToPlugin);const panels  = document.querySelectorAll(&amp;#39;.panel&amp;#39;);const navLinks = document.querySelectorAll(&amp;#39;#pagination a&amp;#39;);// 1200px 이상에서만 작동되도록let isDesktop = window.matchMedia(&amp;#39;(min-width: 1200px)&amp;#39;).matches;const mql = window.matchMedia(&amp;#39;(min-width: 1200px)&amp;#39;);let isScrolling = false;let currentIndex = 0;// 해시로 진입했을 때 처리 (#1, #2 ...)initFromHash();// 뷰포트 변경 시 데스크톱 여부 갱신mql.addEventListener(&amp;#39;change&amp;#39;, (e) =&amp;gt; {  isDesktop = e.matches;});// 스크롤시 navigation 메뉴 active 처리 및 location.hash 처리function setActive(idx) {  navLinks.forEach(a =&amp;gt; a.classList.remove(&amp;#39;on&amp;#39;));  if (navLinks[idx]) navLinks[idx].classList.add(&amp;#39;on&amp;#39;);   const hash = &amp;#39;#&amp;#39; + (idx + 1);  if (location.hash !== hash) {    // replaceState 쌓이지 않도록    history.replaceState(null, &amp;#39;&amp;#39;, hash);  }}// 해시로 들어왔을 때 초기 위치 맞추기function initFromHash() {  const h = location.hash.replace(&amp;#39;#&amp;#39;, &amp;#39;&amp;#39;);  const num = parseInt(h, 10);  if (!isNaN(num) &amp;amp;&amp;amp; num &amp;gt;= 1 &amp;amp;&amp;amp; num &amp;lt;= panels.length) {    currentIndex = num - 1;    gsap.set(window, { scrollTo: panels[currentIndex] });    setActive(currentIndex);  } else {    setActive(0);  }}// pc에서만 휠로 섹션 이동window.addEventListener(&amp;#39;wheel&amp;#39;, (e) =&amp;gt; {  if (!isDesktop) return;  if (isScrolling) return;  e.preventDefault();  if (e.deltaY &amp;gt; 0 &amp;amp;&amp;amp; currentIndex &amp;lt; panels.length - 1) {    currentIndex++;  } else if (e.deltaY &amp;lt; 0 &amp;amp;&amp;amp; currentIndex &amp;gt; 0) {    currentIndex--;  } else {    return;  }  scrollToPanel(currentIndex);}, { passive: false });// navigation 메뉴 클릭navLinks.forEach((a, i) =&amp;gt; {  a.addEventListener(&amp;#39;click&amp;#39;, (e) =&amp;gt; {    e.preventDefault();    if (isScrolling || i === currentIndex) return;    currentIndex = i;    scrollToPanel(currentIndex);  });});// 스크롤 이동 효과 (gsap)function scrollToPanel(index) {  isScrolling = true;  if (isDesktop) disableScroll();  gsap.to(window, {    duration: 1.1,    scrollTo: panels[index],    ease: &amp;quot;power3.inOut&amp;quot;,    onComplete() {      setActive(index);      setTimeout(() =&amp;gt; {        isScrolling = false;        enableScroll();      }, 200);    }  });}// 이동하는 도중에는 스크롤 잠금function disableScroll() {  window.addEventListener(&amp;#39;wheel&amp;#39;, prevent, { passive: false });  window.addEventListener(&amp;#39;touchmove&amp;#39;, prevent, { passive: false });  window.addEventListener(&amp;#39;keydown&amp;#39;, preventForKeys, { passive: false });}function enableScroll() {  window.removeEventListener(&amp;#39;wheel&amp;#39;, prevent, { passive: false });  window.removeEventListener(&amp;#39;touchmove&amp;#39;, prevent, { passive: false });  window.removeEventListener(&amp;#39;keydown&amp;#39;, preventForKeys, { passive: false });}function prevent(e) { e.preventDefault(); }function preventForKeys(e) {  const keys = [32,33,34,35,36,37,38,39,40];  if (keys.includes(e.keyCode)) e.preventDefault();}</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/32</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/32</comments>
<pubDate>Wed, 05 Nov 2025 08:32:38 +0900</pubDate>
</item>
<item>
<title>[Javascript] swiper 첫번째 슬라이드 시작 위치 변경하기(offset)</title>
<link>https://note.chanyeongpark.com/posts/jquery/31</link>
<description>swiper로 centeredSlides 를 설정하지 않는 경우, 첫번재 슬라이드 시작 위치를 아래 화면과 같이 변경 하고자 할때, swiper의 offset 기능을 활용하면 된다.(아래 화면은 slidesPerView를 5로 설정하고, 화면에 3개의 슬라이드 보여지도록 레이아웃을 구성한 경우다)swiper 코드const el = document.querySelector(&amp;#39;.m4 .roll_wrap&amp;#39;);// 한 칸(슬라이드 너비 + 간격) 계산const calcSlot = () =&amp;gt; {  const slide = el.querySelector(&amp;#39;.swiper-slide&amp;#39;);  if(!slide) return 0;  const w = slide.getBoundingClientRect().width;  return w + 20; // spaceBetween: 20 과 동일하게};let offset = 0;const swiper = new Swiper(&amp;#39;.m4 .roll_wrap&amp;#39;, {  loop: true,  pagination: {    el: &amp;#39;.m4 .dots&amp;#39;,    clickable: true  },  allowTouchMove: true,   // 모바일/PC 드래그 모두 허용  simulateTouch: true,    // 마우스로 드래그 허용  grabCursor: true,      // 마우스 올렸을 때 손바닥 커서  speed: 500, // 슬라이드 전환 속도  slidesOffsetBefore: 0,  on: {    init(sw){      offset = calcSlot();      sw.params.slidesOffsetBefore = offset;      sw.update();    },    resize(sw){      offset = calcSlot();      sw.params.slidesOffsetBefore = offset;      sw.update();    },    slideChangeTransitionEnd: function () {         }  },  spaceBetween: 20, // 간격  breakpoints: {    1200: {      slidesPerView: 5 // PC    },    750: {      slidesPerView: 3 // 태블릿    },    0: {      slidesPerView: 2 // 모바일    }  },});</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/31</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/31</comments>
<pubDate>Sat, 01 Nov 2025 13:30:46 +0900</pubDate>
</item>
<item>
<title>[Javascript] swiper 슬라이드 개수가 모자란 경우 특정 엘리먼트를 찾아 숨김 처리하기</title>
<link>https://note.chanyeongpark.com/posts/jquery/30</link>
<description>swiper 를 사용하는 영역에서 navigation 요소가 삽입될 부모 엘리먼트를 하나 만들어 놨는데,ex) &amp;lt;div class=&amp;quot;arrows_wrapper&amp;quot;&amp;gt;&amp;lt;div class=&amp;quot;arrows&amp;quot;&amp;gt;.....navigation 이 곳에 삽입.....&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;swiper 사용시 설정된slidesPerView 보다 slide의 개수가 적은 경우 navigation 은 swiper에서 노출 시키지 않지만,arrows_wrapper 부모 엘리먼트는 그대로 남아있어.arrows_wrapper에 설정된 margin, padding등의 스타일로 인해 swiper 하단에 불필요한 공백이 생기는 상황.아래 코드는 swiper의 슬라이드 개수가slidesPerView가 적은 경우arrows_wrapper 엘리먼트를 찾아 숨김 처리하는 코드.swiper 코드const swiper = new Swiper(wrap.querySelector(&amp;#39;.itemlist&amp;#39;), {  loop: false,  navigation: {    prevEl: wrap.querySelector(&amp;#39;.arrows_wrapper .arrows .prev&amp;#39;),    nextEl: wrap.querySelector(&amp;#39;.arrows_wrapper .arrows .next&amp;#39;),    clickable: true  },  allowTouchMove: true,   // 모바일/PC 드래그 모두 허용  simulateTouch: true,    // 마우스로 드래그 허용  grabCursor: true,      // 마우스 올렸을 때 손바닥 커서  speed: 500, // 슬라이드 전환 속도  spaceBetween: 20, // 간격  breakpoints: {    1200: {      slidesPerView: 4 // PC    },    750: {      slidesPerView: 3 // 태블릿    },    0: {      slidesPerView: 2, // 모바일      spaceBetween: 10, // 간격    }  },  on: {    init(sw){ toggleUi(sw); },    resize(sw){ toggleUi(sw); },    breakpoint(sw){ toggleUi(sw); },    slidesLengthChange(sw){ toggleUi(sw); }  },});swiper on 에서 실행되는 함수 (swiper 코드 아래 삽입)// 슬라이드 개수 적은 경우 특정 요소 숨김 처리function toggleUi(sw) {  const total = getRealSlidesLength(sw);  const perView = getSlidesPerView(sw) * getRows(sw);  const shouldHide = total &amp;lt;= perView;  // 숨겨질 대상. 아래 부분 수정 (sw 기준으로 요소를 찾음.)  const ele = sw.el.closest(&amp;#39;.itemlist_wrap&amp;#39;).querySelector(&amp;#39;.arrows_wrapper&amp;#39;);   if (!ele) return;  ele.style.display = shouldHide ? &amp;#39;none&amp;#39; : &amp;#39;&amp;#39;;}function getSlidesPerView(sw){  const spv = sw.params.slidesPerView;  if (spv === &amp;#39;auto&amp;#39;) return sw.slidesPerViewDynamic(&amp;#39;current&amp;#39;, true) || 1;  return Number(spv) || 1;}function getRows(sw){  return Number(sw.params.grid?.rows || 1) || 1;}function getRealSlidesLength(sw){  // 1) virtual 모드가 &amp;#39;활성화&amp;#39;된 경우에만 사용  const virtualEnabled =    sw.params?.virtual === true ||    sw.params?.virtual?.enabled === true ||    sw.virtual?.enabled === true;  if (virtualEnabled &amp;amp;&amp;amp; Array.isArray(sw.virtual?.slides)) {    return sw.virtual.slides.length;  }  // 2) loop 모드: 원본 개수 사용  if (sw.params?.loop &amp;amp;&amp;amp; Array.isArray(sw.originalSlides)) {    return sw.originalSlides.length;  }  // 3) 기본  return Array.isArray(sw.slides) ? sw.slides.length : 0;}</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/30</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/30</comments>
<pubDate>Sat, 25 Oct 2025 11:39:04 +0900</pubDate>
</item>
<item>
<title>[Javascript] 이미지맵 반응형에서 적용하기 (rect, circle, poly 모두 지원)</title>
<link>https://note.chanyeongpark.com/posts/jquery/29</link>
<description>pc기준으로 이미지맵을 적용한 경우반응형에서 줄어든 이미지의 비율에 따라 자동으로 이미지맵 좌표를 수정해준다.(rect, circle, poly 모두 지원)단, 이미지는 max-width: 100%; height: aut; 처리 되어 있어야 한다.javascript아래와 같이 구현한다.&amp;lt;style&amp;gt;/* 반응형 기본 */img[usemap] { max-width: 100%; height: auto; display:block; }&amp;lt;/style&amp;gt;&amp;lt;script&amp;gt;// 반응형 이미지맵 스케일러(() =&amp;gt; { const imgs = Array.from(document.querySelectorAll(&amp;#39;img[usemap]&amp;#39;)); const parseNums = s =&amp;gt; s.split(&amp;#39;,&amp;#39;).map(n =&amp;gt; parseFloat(n.trim())); const storeOriginalCoords = map =&amp;gt; {  map.querySelectorAll(&amp;#39;area&amp;#39;).forEach(a =&amp;gt; {   if (!a.dataset.origCoords) a.dataset.origCoords = a.coords;  }); }; const scaleArea = (area, rw, rh) =&amp;gt; {  const shape = (area.shape || &amp;#39;rect&amp;#39;).toLowerCase();  const orig = parseNums(area.dataset.origCoords);  if (shape === &amp;#39;rect&amp;#39;) {   // x1,y1,x2,y2   const [x1,y1,x2,y2] = orig;   area.coords = [    Math.round(x1 * rw), Math.round(y1 * rh),    Math.round(x2 * rw), Math.round(y2 * rh)   ].join(&amp;#39;,&amp;#39;);  } else if (shape === &amp;#39;circle&amp;#39;) {   // x,y,r   const [x,y,r] = orig;   const rScaled = Math.round(r * (rw + rh) / 2);   area.coords = [    Math.round(x * rw), Math.round(y * rh), rScaled   ].join(&amp;#39;,&amp;#39;);  } else if (shape === &amp;#39;poly&amp;#39;) {   // x1,y1,x2,y2, ...   const scaled = orig.map((v, i) =&amp;gt; Math.round(v * (i % 2 ? rh : rw)));   area.coords = scaled.join(&amp;#39;,&amp;#39;);  } }; const resizeMapForImage = img =&amp;gt; {  const usemap = img.getAttribute(&amp;#39;usemap&amp;#39;);  if (!usemap) return;  // 연결된 &amp;lt;map&amp;gt; 찾기  const name = usemap.replace(&amp;#39;#&amp;#39;,&amp;#39;&amp;#39;);  const map = document.querySelector(`map[name=&amp;quot;${name}&amp;quot;], map#${name}`);  if (!map) return;  // 원본 좌표 저장  storeOriginalCoords(map);  // 이미지 실제 크기/표시 크기  const naturalW = img.naturalWidth || img.width;  const naturalH = img.naturalHeight || img.height;  const displayW = img.clientWidth;  const displayH = img.clientHeight || Math.round(displayW * (naturalH / naturalW));  if (!naturalW || !naturalH || !displayW || !displayH) return;  const rw = displayW / naturalW;  const rh = displayH / naturalH;  map.querySelectorAll(&amp;#39;area&amp;#39;).forEach(a =&amp;gt; scaleArea(a, rw, rh)); }; // 디바운스 리사이즈 let rid; const onResize = () =&amp;gt; {  cancelAnimationFrame(rid);  rid = requestAnimationFrame(() =&amp;gt; imgs.forEach(resizeMapForImage)); }; // 초기 바인딩 imgs.forEach(img =&amp;gt; {  if (img.complete &amp;amp;&amp;amp; img.naturalWidth) {   resizeMapForImage(img);  } else {   img.addEventListener(&amp;#39;load&amp;#39;, () =&amp;gt; resizeMapForImage(img), { once:true });  } }); window.addEventListener(&amp;#39;resize&amp;#39;, onResize); window.addEventListener(&amp;#39;orientationchange&amp;#39;, onResize);})();&amp;lt;/script&amp;gt;</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/29</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/29</comments>
<pubDate>Thu, 14 Aug 2025 13:46:58 +0900</pubDate>
</item>
<item>
<title>[CSS] ::after 가상요소를 통해 원 그리기 애니메이션 구현</title>
<link>https://note.chanyeongpark.com/posts/html_css/15</link>
<description>swiper 구현 도중 dot에 원이 그려지면 다음 슬라이드로 넘어가는 효과가 필요했다.위 화면과 같이 1px border 의 원이 그려지는 애니메이션을 ::after 가상요소를 통해 구현했다.css코드는 아래와 같다.@property --angle { syntax: &amp;#39;&amp;lt;angle&amp;gt;&amp;#39;; inherits: false; initial-value: 0deg;}.dots span.swiper-pagination-bullet-active::after{ content:&amp;quot;&amp;quot;; position:absolute; inset:0; transition: all 0.1s; border-radius:50%; --thickness:1px; --angle:0deg; background: conic-gradient(rgba(255,255,255,0.8) var(--angle), transparent 0); -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - var(--thickness)), #000 0); mask: radial-gradient(farthest-side, transparent calc(100% - var(--thickness)), #000 0); animation: draw 4s linear forwards;}@keyframes draw{ from{ --angle: 0deg; } to { --angle: 360deg; }}conic-gradient() 는 원형에 색상을 채워주는 그라이언트.그라디언트 위에 mask로 구멍을 뚫어줘서 호 처럼 보이게 하고 그라디언트를 4초 동안 천천히 채워가는 방식이다.</description>
<category>HTML / CSS</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/html_css/15</guid>
<comments>https://note.chanyeongpark.com/posts/html_css/15</comments>
<pubDate>Wed, 13 Aug 2025 14:32:39 +0900</pubDate>
</item>
<item>
<title>[jQuery] slick 사용시 prev/next 버튼 해당하는 경우에만 노출</title>
<link>https://note.chanyeongpark.com/posts/jquery/28</link>
<description>slick 사용시 arrows 옵션을 true 로 설정하면 prev/next 버튼이 노출되는데,첫번째 슬라이드에서는 prev 버튼을 숨기고, 마지막 슬라이드에서는 next 버튼을 숨기는 방법.jQuery아래와 같이 beforeChange 메소드를 통해 구현 가능하다.let $slider = $(&amp;#39;.prdt_box .lef .roll_wrap ul.roll&amp;#39;)$slider = $(&amp;#39;.prdt_box .lef .roll_wrap ul.roll&amp;#39;).slick({  &amp;#39;fade&amp;#39; : false,  &amp;#39;dots&amp;#39; : false,  &amp;#39;arrows&amp;#39; : true,  .......});function updateArrows(nextSlide) {  var slickInstance = $slider.slick(&amp;#39;getSlick&amp;#39;);  // slickInstance가 존재하는지 확인  if (!slickInstance) return;  var totalSlides = slickInstance.slideCount - slickInstance.options.slidesToShow;  $(&amp;#39;.slick-prev&amp;#39;).toggle(nextSlide !== 0);  $(&amp;#39;.slick-next&amp;#39;).toggle(nextSlide &amp;lt; totalSlides);}// 슬라이드 변경 직전에 실행$slider.on(&amp;#39;beforeChange&amp;#39;, function (event, slick, currentSlide, nextSlide) {  updateArrows(nextSlide);});// 초기 상태 업데이트 (슬릭이 완전히 로드된 후 실행)$slider.on(&amp;#39;init&amp;#39;, function (event, slick) {  updateArrows(0);});// 슬릭 초기화 강제 실행 (init 이벤트 트리거)updateArrows(0);</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/28</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/28</comments>
<pubDate>Thu, 27 Feb 2025 19:29:16 +0900</pubDate>
</item>
<item>
<title>[jQuery] .animate() 로 숫자 카운팅 구현하기</title>
<link>https://note.chanyeongpark.com/posts/jquery/27</link>
<description>숫자 0부터 숫자가 커지는 카운팅 효과를 구현해본다.구현된 결과 화면 예시HTML카운팅될 엘리먼트를 추가한다.&amp;lt;ul&amp;gt;  &amp;lt;li&amp;gt;    &amp;lt;div class=&amp;quot;no&amp;quot;&amp;gt;+&amp;lt;strong&amp;gt;1300&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;    &amp;lt;p&amp;gt;일반 참가자&amp;lt;/p&amp;gt;  &amp;lt;/li&amp;gt;  &amp;lt;li&amp;gt;    &amp;lt;div class=&amp;quot;no&amp;quot;&amp;gt;+&amp;lt;strong&amp;gt;520&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;    &amp;lt;p&amp;gt;참가기업&amp;lt;/p&amp;gt;  &amp;lt;/li&amp;gt;  &amp;lt;li&amp;gt;    &amp;lt;div class=&amp;quot;no&amp;quot;&amp;gt;+&amp;lt;strong&amp;gt;230&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;    &amp;lt;p&amp;gt;비즈매칭 참가기업&amp;lt;/p&amp;gt;  &amp;lt;/li&amp;gt;  &amp;lt;li&amp;gt;    &amp;lt;div class=&amp;quot;no&amp;quot;&amp;gt;+&amp;lt;strong&amp;gt;220&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;    &amp;lt;p&amp;gt;비즈매칭 수&amp;lt;/p&amp;gt;  &amp;lt;/li&amp;gt;  &amp;lt;li&amp;gt;    &amp;lt;div class=&amp;quot;no&amp;quot;&amp;gt;+&amp;lt;strong&amp;gt;80&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;    &amp;lt;p&amp;gt;전시 참가 기업&amp;lt;/p&amp;gt;  &amp;lt;/li&amp;gt;  &amp;lt;li&amp;gt;    &amp;lt;div class=&amp;quot;no&amp;quot;&amp;gt;+&amp;lt;strong&amp;gt;30&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;    &amp;lt;p&amp;gt;IR 참가 기업&amp;lt;/p&amp;gt;  &amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;jQuery아래와 같이 코드를 적용한다.해당 객체에 Counter atteibute를 추가하여 값을 0으로 설정한 뒤 animate로 증가시키는 방법이다.$(this).prop(&amp;#39;Counter&amp;#39;, 0).animate({  Counter: $(this).text()}, {  duration: 3000,  easing: &amp;#39;swing&amp;#39;,  step: function (now){    // $(this).text(Math.ceil(now)); // 콤마 없는 숫자 노출 원하는 경우    $(this).text(Math.ceil(now).toLocaleString()); // 콤마 처리된 숫자 노출 원하는 경우  }});</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/27</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/27</comments>
<pubDate>Fri, 11 Oct 2024 17:04:35 +0900</pubDate>
</item>
<item>
<title>nginx에서 http-&gt;https 리디렉션 설정된 경우 let's encrypt 인증서 갱신 안될때</title>
<link>https://note.chanyeongpark.com/posts/linux/31</link>
<description>nginx 를 사용할 때 특정 조건에서 let&amp;#39;s encrypt 인증서가 갱신이 안되는 경우가 있다.나의 경우 http 접속하는 경우 https로 리디렉션 함과 동시에, proxy 를 통해 목적지 서버로 트래픽을 전달하는데, 이 과정에서 webroot를 찾지 못해 갱신이 안되는 문제였다.따라서, 아래와 같이 certbot에서 웹서버를 접근하는 경우 https로 리디렉션 되지 않도록 예외 처리 하였다.기존 문제가 된 구문server {    listen 80;    server_name note.chanyeongpark.com;    index index.html;    root /서버경로/;    return 301 https://note.chanyeongpark.com$request_uri; // 문제가 되는 구문}301로 https로 리디렉션 되고 있는 구문을 아래와 같이 수정하였다.수정된 구문아래와 같이 certbot에서 접근하는 경우 예외처리하여 https로 리디렉션 되지 않도록 처리하였고정상 갱신 되는 것을 확인 하였다.server {    listen 80;    server_name note.chanyeongpark.com;    index index.html;    location /.well-known/acme-challenge/ {        root /서버경로/;    }    location / {        return 301 https://note.chanyeongpark.com$request_uri;    }}</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/31</guid>
<comments>https://note.chanyeongpark.com/posts/linux/31</comments>
<pubDate>Tue, 08 Oct 2024 15:01:14 +0900</pubDate>
</item>
<item>
<title>[PHP] 에디터로 작성한 HTML 코드 중  와  사이에  태그 두개 넣기.</title>
<link>https://note.chanyeongpark.com/posts/php/9</link>
<description>본인이 운영하는 블로그는 다수의 사진이 입력되는 게시글이 많다.오랜 기간 블로그 글을 작성하다 보니, 게시글 마다 사진과 사진 사이 여백이 제각각이다.에디터로 작성된 html코드 중에서 정규식을 통해 &amp;lt;img&amp;gt; 와 &amp;lt;img&amp;gt; 태그 사이를 찾아&amp;lt;br&amp;gt; 태그가 없거나, 2개 미만 이거나, 2개를 초과하는 경우무조건 &amp;lt;br&amp;gt; 두개가 삽입 되도록 코드를 변경하여 출력해 줬다.&amp;lt;img&amp;gt;와 &amp;lt;img&amp;gt;를 찾아 적용모든 &amp;lt;img&amp;gt;와 &amp;lt;img&amp;gt; 사이를 찾아 &amp;lt;br&amp;gt; 두개를 입력해 준다.// 정규식으로 각 &amp;lt;img&amp;gt; 태그 사이에서 &amp;lt;br&amp;gt;을 최대 2개만 남기기 (없거나 2개 미만일 때도 2개로 맞춤)$pattern = &amp;#39;/(&amp;lt;img[^&amp;gt;]*&amp;gt;)(\s*(&amp;lt;br\s*\/?&amp;gt;\s*)*)/i&amp;#39;;$replacement = &amp;#39;$1&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;#39;;$result = preg_replace($pattern, $replacement, $html);echo $result;&amp;lt;img class=&amp;quot;lazy&amp;quot;&amp;gt;와 &amp;lt;img class=&amp;quot;lazy&amp;quot;&amp;gt; 사이만 찾아 적용img 태그에 class=&amp;quot;lazy&amp;quot; 와 같이 특정한 조건에 부합하는 경우에만 적용한다.// 정규식으로 class=&amp;quot;lazy&amp;quot;가 있는 &amp;lt;img&amp;gt; 태그에만 적용$pattern = &amp;#39;/(&amp;lt;img[^&amp;gt;]*class=&amp;quot;lazy&amp;quot;[^&amp;gt;]*&amp;gt;)(\s*(&amp;lt;br\s*\/?&amp;gt;\s*)*)/i&amp;#39;;$replacement = &amp;#39;$1&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;#39;;$result = preg_replace($pattern, $replacement, $html);echo $result;</description>
<category>PHP</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/php/9</guid>
<comments>https://note.chanyeongpark.com/posts/php/9</comments>
<pubDate>Mon, 23 Sep 2024 17:16:42 +0900</pubDate>
</item>
<item>
<title>[HTML] text input에  입력 허용할 문자 지정하기 (inputmode, pattern Attribute)</title>
<link>https://note.chanyeongpark.com/posts/html_css/14</link>
<description>이를테면, &amp;lt;input type=&amp;quot;number&amp;quot;&amp;gt;로 지정하는 경우 오로지 숫자만 입력 가능하여 하이픈(-) 입력이 불가 하다는 문제점이 있다.대안으로 아래와 같이 input type을 text로 두고 정규식으로 허용할 문자를 지정할 수 있다.text input에서 inputmode, pattern 지정&amp;lt;input type=&amp;quot;text&amp;quot; inputmode=&amp;quot;numeric&amp;quot; pattern=&amp;quot;[0-9\-]*&amp;quot; placeholder=&amp;quot;숫자와 하이픈 입력 가능&amp;quot;&amp;gt;</description>
<category>HTML / CSS</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/html_css/14</guid>
<comments>https://note.chanyeongpark.com/posts/html_css/14</comments>
<pubDate>Fri, 13 Sep 2024 10:34:45 +0900</pubDate>
</item>
<item>
<title>[Javascript] ios 사파리에서 CSS 100vh 높이가 제대로 적용되지 않을 때</title>
<link>https://note.chanyeongpark.com/posts/jquery/26</link>
<description>ios 사파리의 경우 100vh 높이가 더 크게 표현되는 오류가 있다.실제 브라우저 height 만큼 100vh가 인식되도록 하기 위해 javascript로 css 변수를 만들어주면 제대로 표현 가능하다.javascript로 100vh 변수 생성하기CSS에서 사용할 수 있도록 브라우저 높이를 계산하여 변수로 전달한다.// ios 에서 100vh 계산 위한 변수 생성function setDynamicHeight() {  let vh = window.innerHeight * 0.01;  document.documentElement.style.setProperty(&amp;#39;--vh&amp;#39;, `${vh}px`);}// 초기 실행 및 창 크기 변경 시 업데이트setDynamicHeight();window.addEventListener(&amp;#39;resize&amp;#39;, setDynamicHeight);CSS 에서 변수 사용하여 100vh 표현하기아래와 같이 javscript로 만든 --vh 를 활용할 수 있다.max-height: calc(var(--vh, 1vh) * 100 - 170px);</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/26</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/26</comments>
<pubDate>Wed, 28 Aug 2024 15:18:16 +0900</pubDate>
</item>
<item>
<title>[jQuery] datepicker 로 check in/out 구현하기</title>
<link>https://note.chanyeongpark.com/posts/jquery/25</link>
<description>여행, 예약사이트에서 흔히 볼 수 있는 check in/out 을 jQuery UI의 Datepicker로 구현하는 방법을 안내한다.input 두개를 만들어 각각 datepicker 가 트리거 되도록 하여 구현한다.HTMLcheck in 과 out input 두개를 만든다.&amp;lt;input type=&amp;quot;text&amp;quot; datepicker checkin&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; datepicker checkout&amp;gt;jQuery각 input에 대해 아래와 같은 옵션으로 datepicker 를 적용한다.// check in/outlet datepicker_checkin = $(&amp;#39;input[datepicker][checkin]&amp;#39;).datepicker({  dateFormat: &amp;#39;yy-mm-dd&amp;#39;,  minDate: 0,  onSelect: function(selectedDate) {    let $this = $(this);    let date = $(this).datepicker(&amp;#39;getDate&amp;#39;);    date.setDate(date.getDate()); // 당일치기도 가능한 경우    // date.setDate(date.getDate() + 1); // 무조건 1박 이상 가능한 경우    datepicker_checkout.datepicker(&amp;quot;option&amp;quot;, &amp;quot;minDate&amp;quot;, date);    // check in 날짜 선택하는 경우 자동으로 check out Datepicker를 띄움    setTimeout(function() {      $this.parent().find(&amp;#39;input[datepicker][checkout]&amp;#39;).datepicker(&amp;#39;show&amp;#39;);    }, 10);  }});let datepicker_checkout = $(&amp;#39;input[datepicker][checkout]&amp;#39;).datepicker({  dateFormat: &amp;quot;yy-mm-dd&amp;quot;,  minDate: 1});</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/25</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/25</comments>
<pubDate>Sat, 20 Jul 2024 10:38:08 +0900</pubDate>
</item>
<item>
<title>[nginx] ip로 접속하지 못하도록 설정</title>
<link>https://note.chanyeongpark.com/posts/linux/30</link>
<description>아이피로 접속하는 경우 444에서가 노출되도록 하는 방법.ip주소 접근 차단vi로 default.conf를 연다. (기본 virtualhosts 설정)vi /etc/nginx/conf.d/default.confdefault.conf 파일 최상단에 아래와 같이 추가한다.# ip 접근을 막기 위한 설정(http)server {  listen 80 default_server;  server_name _;  return 444;}# ip 접근을 막기 위한 설정(https)server {  listen 443 ssl default_server;  server_name _;  ssl_certificate /etc/letsencrypt/live/본인의 ssl경로/fullchain.pem;  ssl_certificate_key /etc/letsencrypt/live/본인의 ssl경로/privkey.pem;  return 444;} https 설정에서 ssl 인증서 경로는 기존에 virtualhost 를 위해 생성해둔 아무 인증서를 연결해준다. # systemctl restart nginx nginx를 재부팅.아이피주소로 접속한 경우 위와 같이 444 에러가 리턴되는 것을 확인할 수 있다.</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/30</guid>
<comments>https://note.chanyeongpark.com/posts/linux/30</comments>
<pubDate>Mon, 22 Apr 2024 11:39:34 +0900</pubDate>
</item>
<item>
<title>[시놀로지] MailPlus Server 에서 AWS SMTP(SES)를 발송 릴레이 서버로 사용하기</title>
<link>https://note.chanyeongpark.com/posts/synology/12</link>
<description>시놀로지 NAS로 메일서버 구축시 문제점시놀로지 NAS는 대부분 가정에서 운영된다.시놀로지 메일플러스(MailPlus Server)를 통해 메일서버를 운영할때 가정에 제공되는 IP가 불량 아이피일 가능성이 클 뿐만 아니라,수신 메일서버에서 스팸으로 필터링 되지 않기 위한 DKIM, whilte domain등 사전 작업은 만만치 않은 일이다.AWS에서 제공하는 SES(Simple Email Service)에서 SMTP를 제공받아시놀로지 Mail plus의 릴레이 서버로 연동한다면 이 복잡한 작업을 AWS에 의존하여 메일서버를 운영할 수 있게 된다.리전 선택 및 도메인 등록SES 공식 웹페이지 https://ap-northeast-2.console.aws.amazon.com/ses/home 을 방문하여 리전을 서울로 변경한다.도메인 등록을 위해 [구성 - 자격 증명] - [자격 증명 생성]을 클릭한다.버튼을 클릭하여 자격 증명 화면에 접속한 다음&amp;#39;보안 인증 유형&amp;#39;을 &amp;#39;도메인&amp;#39;으로 선택하고 &amp;#39;도메인&amp;#39; 입력 란에 이메일에 사용되는 도메인을 입력한다.(ex: email@chanyeongpark.com 인 경우chanyeongpark.com 입력)자격 증명 생성 과정에서 DKIM은 기본적으로 설정되니 따로 건드릴 설정 없이바로 [자격 증명 생성] 버튼을 클릭하여 생성을 완료한다.도메인 DKIM 레코드 설정도메인에 대한 메일 신뢰도 검증을 위한 DKIM 설정을 위해생성된 자격 증명 상세 화면으로 이동한다.도메인 DNS에서 등록한 CNAME 레코드 값을 확인할 수 있다.도메인에 레코드 등록을 위해 DNS 관리 화면으로 이동한다.나의 경우 AWS route53 서비르를 통해 레코드를 추가했으나, 가비아, 후이즈 등 타사 DNS 관리 서비스의 레코드 등록 방법도 route53과 크게 다르지 않다.route53 서비스로 들어가 chanyeongpark.com 에 대한 [레코드 생성]을 클릭한다.위와 같이 &amp;#39;레코드 유형&amp;#39;을 CNAME으로 선택한 다음앞서 SES 에서 발급 받은 레코드 이름과 값을 복사하여 붙여넣는다.발급 받은 3개의 레코드 모두 생성해 준다.SES SMTP 발급도메인 DKIM 레코드 등록이 완료 되었다면, 메일 발송을 하기 위한 SMTP를 발급 받는다.발급 받을 SMTP가 시놀로지와 연동될 정보다.[SMTP 설정 - SMTP 보안 인증 생성] 을 클릭하면,AWS IAM(인증 정보 관리) 자격 증명 생성 화면으로 이동하게 된다.SMTP 자격증명 정보가 자동으로 입력되므로 [사용자 생성] 을 클릭하여 자격 증명을 생성한다.자격증명 생성이 완료되면 위와 같이 자격 증명 정보가 표시되는데,이 정보가 시놀로지와 SES가 연동될 인증 정보다.SMTP 비밀번호는 이 화면에서만 확인 하거나 다운로드 가능하며페이지를 벗어나는 경우 다시 확인할 수 없으니 반드시 메모장에 기록해 둔다.시놀로지 MailPlus Server 에서 SES SMTP 연동AWS SES 에서 SMTP 발급까지 완료 되었다면 시놀로지에서 발급 받은 SMTP를 릴레이로 연동할 순서다.시놀로지 DMS에 접속하여 [Synology MailPlus Server] 패키지를 실행한 다음[메일 배달 - 제공] 으로 이동하여 위와 같이 설정한 다음 [적용]을 클릭한다.이후부터 시놀로지에서 메일을 발송하는 경우 NAS에서 직접 발송하지 않고,AWS SES의 SMTP로 릴레이하여 발송하게 된다.</description>
<category>SSynology</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/synology/12</guid>
<comments>https://note.chanyeongpark.com/posts/synology/12</comments>
<pubDate>Wed, 27 Mar 2024 11:36:58 +0900</pubDate>
</item>
<item>
<title>[nginx] reverse proxy 에서 프록시 캐시서버(CDN) 적용하기 (centos 7)</title>
<link>https://note.chanyeongpark.com/posts/linux/28</link>
<description>사진등 용량이 큰 리소스를 별도 서버에서 reverse proxy하여 웹사이트에 노출시키고 있다면접속자에게 1M 용량의 이미지를 노출시킬 때 프록싱간 in/out bound 를 합쳐 트래픽을 총 3M를 소진하게 되는데,접속자를 통해 한번 호출된 파일을 프록시 서버에 캐시파일로 저장해 두고 반환한다면 out-bound 트래픽 1M만 소진하게 되어트래픽을 1/3 수준으로 절감할 수 있다.이를 Cache server (CDN)이라고 하며,nginx 에서는 간단한 설정으로 캐시서버 구축이 가능하다.캐시서버가 필요하게 된 배경내가 운영중인 사진 블로그는 웹서버와 사진이 보관된 스토리지 서버가 분리되어 있으며, 글 마다 노출되는 사진 파일이 총 10M이상 된다.접속자 한명이 블로그 글 하나를 조회하게 되면 프록시 서버와 스토리지서버(사진이 보관된)간 통신하는 과정에총 30M 이상의 트래픽을 소진하게 되는데 100명의 접속자가 글을 조회 한다면 3G 이상의 트래픽을 유발 시킨다.[접속자] &amp;lt;-- out 10M 소진 -- [프록시서버] &amp;lt;-- in/out 20M --&amp;gt; [스토리지서버]블로그 글에서 제공되는 사진은 10M지만, 총 30M 트래픽을 소진하게 됨.따라서, 캐시서버를 구축하여 이미지 파일을 프록시서버에 보관하게 된다면트래픽이 스토리지 서버까지 다녀올 필요가 없기 때문에 클라이언트에게 out-bound 되는 10M만 소진하면 된다.nginx.conf 설정먼저, 캐시와 temp등의 경로 설정을 위해 nginx.conf 파일을 수정한다.#/etc/nginx/nginx.confvi로 설정 파일을 열어 아래와 같이 설정한다....http {  ...  proxy_cache_path /etc/nginx/cache/ levels=1:2 keys_zone=cache_zone:40m inactive=7d max_size=100m;  proxy_temp_path /etc/nginx/cache_temp/;  include /etc/nginx/conf.d/*.conf;  ...}1) proxy_cache_path : 캐시 저장소 경로를 설정한다.- levels : 하위 디렉토리의 깊이- key_zone : 캐시존 이름- inactive : 캐시 파일이 정해진 기간만큼 사용되지 않는다면 캐시에서 제거2)proxy_temp_path : temp 파일이 저장될 경로를 설정한다.vietualhost 설정특정 호스팅에 캐시를 적용하기 위해 conf 파일을 열어 세부 설정을 한다.# vi /etc/nginx/conf.d/000.net.conf(virtual host 설정을 위해 생성한 conf 파일을 연다.)virtual host 의 conf 파일을 vi로 연 다음아래와 같이 작성한다.server {  location / {    ...   # cache    proxy_cache cache_zone;    # cache data 7일 동안 보관    expires 7d;    # cache 업데이트 충돌시 잠금시간    proxy_cache_lock_timeout 5s;    # cache method 설정    proxy_cache_methods GET HEAD POST;    # cache 결과코드를 20분동안 보관    proxy_cache_valid 200 401 301 304 20m;    # cache 상태를 헤더로 보여줌. (MISS, HIT 등)    add_header X-Proxy-Cache $upstream_cache_status;    # cache-control 설정    add_header Cache-Control &amp;quot;public&amp;quot;;    # 다음 클라이언트 헤더 무시    proxy_ignore_headers X-Accel-Expires Expires Cache-Control;    ...  }}설정이 완료 되었다면 nginx 를 재시작 한다.# systemctl restart nginx브라우저에서 캐싱 여부 확인하기설정을 모두 마쳤다면, 프록싱이 적용된 사이트를 접속하여 브라우저 작업관리자를 통해 캐싱 여부를 확인해본다.1) 최초 요청으로 캐싱되지 않은 이미지가 노출된 경우 &amp;gt; X-Proxy-Caches : MISS 노출2) 캐싱이 적용된 이미지인 경우 &amp;gt; X-Proxy-Caches : HIT 노출</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/28</guid>
<comments>https://note.chanyeongpark.com/posts/linux/28</comments>
<pubDate>Wed, 27 Mar 2024 10:58:40 +0900</pubDate>
</item>
<item>
<title>CentOS 에서 CPU 온도 체크하기 (lm_sensors)</title>
<link>https://note.chanyeongpark.com/posts/linux/27</link>
<description>centos(리눅스)에서는 간편하게 cpu 온도를 확인할 수 있는 모니터링 툴을 제공한다.ln_sensors 설치cpu 온도 모니터링 툴 lm_sensors 를 yum을 통해 설치한다.# yum install lm_sensors설치 후 대상 하드웨어를 인식 시킨다.다만, vm 환경에서는 하드웨어 인식이 안될 수 있으니 lm_sensors 사용 가능 여부를 확인한다.# sensors-detectln_sensors 사용 방법lm_sensors는 명령어 한번으로 간편하게 사용 가능하다.# sensorssensors 명령어만 치면 바로 모니터링 할 수 있다.- Package : cpu의 평균 온도를 보여준다.- Core : 각 Core별 온도를 보여준다. CPU Core 개수에 따라 노출되는 개수가 다르다.</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/27</guid>
<comments>https://note.chanyeongpark.com/posts/linux/27</comments>
<pubDate>Tue, 26 Mar 2024 12:56:52 +0900</pubDate>
</item>
<item>
<title>route53 상태검사(health check)로 내 서버 접속상태 수시로 체크하기</title>
<link>https://note.chanyeongpark.com/posts/aws/5</link>
<description>모든 리전(국가)에서의 내 서버의 접속 상태를 수시로 자동 체크해주는 route53 &amp;#39;상태검사(health check)&amp;#39; 기능을 소개한다.지정해 놓은 외부 접속 주소를 route53이 수시로 체크 해주고,접속 불능 상태가 되었다면 이메일로 알림 메일을 보내준다.route53 상태 검사 생성AWS route53 콘솔을 방문한 다음 (https://us-east-1.console.aws.amazon.com/route53/home)좌측 메뉴에서 [상태 검사]를 클릭한다.상단 메뉴바에서 [상태 검사 생성]을 클릭한다.위와 같이 상태 체크 정보를 입력후 [다음]을 클릭하면 즉시 상태 검사를 수행한다.1) 모니터링 대상 : 외부 목적지의 접속을 시도해야 하기 때문에 &amp;#39;엔드포인트&amp;#39; 선택2) 엔드포인트 지정 기준 : &amp;#39;도메인 이름&amp;#39;을 선택하여 도메인 주소로 접속 시도 요청. 접속 시도할 정보를 입력해준다.상태 체크가 시작되면 아래와 같이 현황을 보여준다.접속 리전 설정상태 검사할 접속 국가를 설정하려면, 상태 검사 생성 과정에서[고급 구성]을 클릭하여 리전을 선택해준다.상태 체크 비용route53 의 상태체크 기능은 엔드포인트 개수에 따른 비용이 과금된다.https://aws.amazon.com/ko/route53/pricing/#Health_Checks나는 AWS 외 엔드포인트로 설정 하였기 때문에월별 0.75USD 비용이 청구된다. 한화로 천원 가량. https로 요청한다면 2,600원 가량.(세상에 공짜는 없다.)</description>
<category>AWS</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/aws/5</guid>
<comments>https://note.chanyeongpark.com/posts/aws/5</comments>
<pubDate>Tue, 26 Mar 2024 12:47:32 +0900</pubDate>
</item>
<item>
<title>centos7 reverse proxy    nginx   </title>
<link>https://note.chanyeongpark.com/posts/linux/26</link>
<description>하나의 아이피로 연결된 같은 네트워크내 다수의 서버를nginx reverse proxy 서버를 통해 프록싱 해본다.reverse proxy가 필요한 상황나의 경우 아래 두가지 이유 때문에 리버스프록시 서버가 필요한 상황이었다.1) 다수의 서버가 연결되어있는 게이트웨이에 단 하나의 외부 아이피만 존재하는 상황.외부 트래픽이 유입하면 reverse proxy 서버를 통해 사설아이피로 트래픽을 유도하여 특정 서버로 유입되도록 한다.2) 내가 운영중인 메인서버의 공인IP가 외부로 유출되면 DDOS 공격에 취약해지기 때문에 공인 IP를 은폐할 목적.AWS에 프록시 서버를 하나 더 두고 내 서버를 프록싱 하면 웹사이트의 외부 IP는 AWS의 IP로 노출된다.프록시 서버로 nginx를 선택한 이유nginx는 apache의 대량 트래픽 처리시 발생하는 부하 문제를 보완하기 위해 탄생한 웹서버다.즉, 다수 서버에 유입될 트래픽이 모두 거쳐가게 되는 프록시 서버에는 nginx만큼 효율이 좋은 웹서버는 없었기 때문이다.심지어 apache보다 가볍고, 설정 또한 직관적이고 쉽다.최소 사양으로 구동되는 프록시 서버에 nginx보다 적합한 웹서버는 없다는 생각이다.nginx 설치먼저, nginx를 설치한다.내가 사용중인 centos7 OS는 공식 yum서버에서 nginx를 제공하지 않기 때문에별도의 repo를 yum에 외부 저장소를 등록해주고 nginx를 설치한다.# vi /etc/yum.repos.d/nginx.repoyum.repos.d 디렉토리 내에 repo 파일을 하나 만들어 주고,아래와 같이 vi로 작성하여 저장한다.[nginx]name=nginx repobaseurl=http://nginx.org/packages/centos/7/$basearch/gpgcheck=0enabled=1외부 저장소 등록이 완료 되었다면 yum 을 통해 nginx를 설치한다.# yum install nginx클라이언트 최대 전송량 설정클라이언트가 nginx 프록시 서버에서 목적지 서버로 요청할 때 body의 최대 전송량을 설정해준다.nginx의 기본 client_max_body_size는 1M로, 허용량을 초과하게 되면 요청 자체가 되지 않기 때문에 반드시 늘려줘야 한다.client_max_body_size를 모든 호스트에 대해 적용해 주려면# vi /etc/nginx/nginx.conf를 vi로 열어http {  ... client_max_body_size 40000M;  ...}를 추가해 주거나,호스팅 개별적으로 적용해 주려면# vi /etc/nginx/conf.d/[hostname].conf를 vi로 열어server {  ... client_max_body_size 40000M;  ...}를 추가해준다.reverse proxy 구성을 위한 설정reverse proxy 구성을 위해 설정파일을 생성해야 한다.#cd/etc/nginx/conf.dnginx 설정 파일은/etc/nginx/conf.d/ 경로내에 저장한다.파일명은 아무렇게나 설정해도 되지만, 반드시 확장자는 .conf 로 저장해야 한다.chanyeongpark.com 도메인에 대한 virtual host 및 reverse proxy 설정을 위해 아래와 같이 새로운 설정 파일을 생성한다.나는 식별이 용이하도록 도메인 이름으로 설정 파일을 생성했다.#vi /etc/nginx/conf.d/note.chanyeongpark.com.confvi 로 설정파일에 아래와 같이 내용을 작성했다.server {    listen 80;    #80번 포트로 들어오는 경우에만 실행    server_name note.chanyeongpark.com;     #접속 주소가note.chanyeongpark.com 인 경우에만 실행    # root /home/note.chanyeongpark.com/public_html;     #virtual host 경로 설정    # index index.html     #index 파일명 설정    return 301 https://note.chanyeongpark.com;    #80번포트로 접속한 경우 https://로 리다이렉팅}server {    listen 443 ssl;    #443포트로 접속한 경우에만 실행    server_name note.chanyeongpark.com;     #접속 주소가note.chanyeongpark.com 인 경우에만 실행    root /home/note.chanyeongpark.com/public_html;     #virtual host 경로 설정    error_log /home/note.chanyeongpark.com/error_log;     #error log 파일 위치    access_log /home/note.chanyeongpark.com/access_log;     #access log 파일 위치    ssl_certificate /etc/letsencrypt/live/note.chanyeongpark.com/fullchain.pem;     #ssl 적용을 위한 pem 위치    ssl_certificate_key /etc/letsencrypt/live/note.chanyeongpark.com/privkey.pem;     #ssl 적용을 위한 privatekey 위치    location / {        proxy_pass http://111.222.333.444/;     #111.222.333.444를 목적지로 포워딩 한다.        proxy_http_version 1.1;     #proxy http 버전을 1.1로 설정(nginx 공식 문서에서 1.1로 권장함, 기본값은 1.0)        proxy_set_header Host $http_host;     #http host 정보를 header로 전달한다.        proxy_set_header X-Real-IP $remote_addr;     #요청 클라이언트의 IP를 header로 전달한다.        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;        proxy_set_header X-Forwarded-Proto $scheme;    }}http로 접근한 경우 ssl이 적용된 도메인으로 리다이렉트 되어야 하기 때문에virtual host 경로를 지정하여 인증서를 발급(재발급) 받을 수 있도록 하였으며,http 접속시 https로 301 리다이렉트 되도록 구성 하였다.443포트로 접속시에는 요청 클라이언트의 정보를 header로 실어 목적지 서버로 전달할 수 있도록 설정하였다.# root /home/note.chanyeongpark.com/public_html;# index index.html위 구문은 let&amp;#39;s encrypt에서 인증서 발급시 http로 접속하는 경우 홈디렉토리로 연결 되어야 하기네 필요한 구문으로,ssl 연결 이후 연장할 때는 불필요하므로 주석처리 해뒀다.proxy_http_version  기본값 1.0으로 사용시 시놀로지 DSM을 reverse proxy 하는 경우 모바일에서 js, css 등 리소스를 로드하지 못하는현상이 발견 되었다.</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/26</guid>
<comments>https://note.chanyeongpark.com/posts/linux/26</comments>
<pubDate>Mon, 25 Mar 2024 20:50:12 +0900</pubDate>
</item>
<item>
<title>리눅스 실시간 모니터링 도구 htop 사용하기 </title>
<link>https://note.chanyeongpark.com/posts/linux/25</link>
<description>기본적으로 리눅스에는 실시간 모니터링을 위한 도구인 &amp;#39;top&amp;#39; 을 제공한다.# toptop 을 실행하면 아래와 같은 화면의 모니터링 정보가 노출 되는데cpu, 메모리, 로드율등 기본적인 현황을 확인할 수 있다.htop으로 보다 세부적인 모니터링htop 은 top 보다 세부적인 실시간 모니터링 정보를 확인할 수 있는 도구로아래와 같이 yum으로 설치하여 사용할 수 있다.# yum install epel-release# yum install htop# htop실행 화면은 다음과 같은데,cpu의 각 스레드별로 사용률을 확인할 수 있고,다양한 설정을 통해 실시간 내역을 명확하게 확인 가능하다.</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/25</guid>
<comments>https://note.chanyeongpark.com/posts/linux/25</comments>
<pubDate>Mon, 25 Mar 2024 09:19:03 +0900</pubDate>
</item>
<item>
<title>route53 에서 '루트 도메인' 접속시 'www.도메인' 으로 리다이렉트 시키는 방법 (feat. cloudfront + s3 + ACL)</title>
<link>https://note.chanyeongpark.com/posts/aws/4</link>
<description>route53 에 자신의 도메인을 등록하여 dns 서비스를 이용하는 경우 dns 국제 규약에 의거 루트 도메인에는 cname을 적용할 수 없는데루트 도메인에 cname을 등록해야만 하는 상황인 경우 (chanyeongpark.com 과 www.chanyeongpark.com 모두 cname을 적용하고 싶을 때),대안으로 cloud-front 와 s3를 활용하여 &amp;#39;www.chanyeongpark.com&amp;#39; 으로 포워딩 시킬 수 있는 방법이 있다.루트도메인에 cname을 적용해야 하는 배경나는 홈서버를 운영중이다.가정용 인터넷 회선은 이따금씩 할당 받는 ip가 변경되기 때문에 필연적으로 DDNS 서비스를 사용하여 변경된 ip를 도메인으로 참조해야 한다. (0000.synology.me)아이피가 변경될 때 마다 도메인이 바라보는 A 레코드 아이피를 바꿔줄 수 없으니 당연히 레코드를 cname 으로 설정하여 0000.synology.me 를 참조하도록 설정하고 있다.최근 AWS route53 으로 도메인 네임서버를 이관 하였는데,route53은 루트 도메인(chanyeongpark.com)에 cname을 설정할 수 없어 곤란한 상황.AWS 여러 서비스를 살펴보니, 오브젝트스토리지 서비스인 &amp;#39;s3&amp;#39;와 CDN 서비스인 &amp;#39;cloudfront&amp;#39; 서비스를 조합하면루트도메인(chanyeongpark.com) 접속시 &amp;#39;www.chanyeongpark.com&amp;#39;으로 리다이렉트 시켜 대응할 수 있다는 사실을 알았다.작동되는 시나리오chanyeongpark.com 접속시 리다이렉트가 작동되는 시나리오는 아래와 같다.[chanyeongpark.com 요청] -&amp;gt; [cloudfront : https 프록싱] -&amp;gt; [s3 : http 연결] -&amp;gt; [www.chanyeongpark.com redirect]접속자가 도메인 요청시 cloudfront endpoint로 연결되며, cloudfront는 다시 s3의 정적 웹사이트 endpoint로 연결하면, s3가 정적 웹사이트에 설정된 url로 redirect하는 방식이다.s3 버킷 생성https://s3.console.aws.amazon.com/s3/get-started?region=ap-northeast-2&amp;amp;bucketType=general&amp;amp;region=ap-northeast-2AWS s3는 &amp;#39;정적호스팅&amp;#39; 기능을 제공한다. s3 버킷에 업로드된 파일을 웹에서 열 수 있도록 url을 제공하는 기능인데,이 정적호스팅은 &amp;#39;정적 웹 사이트 호스팅&amp;#39; 과 &amp;#39;객체에 대한 요청 리디렉션&amp;#39; 두가지 속성을 제공한다.1) 정적 웹 호스팅 : 저킷 접속시 버킷에 업로드된 특정 html 파일이 열리도록 하는 옵션2) 객체에 대한 요청 리디렉션 : 버킷 접속시 다른 버킷이나 외부 url로 리디렉션 시키는 옵션s3 버킷 접속시 &amp;#39;www.chanyeongpark.com&amp;#39;으로 접속될 수 있도록 &amp;#39;객체에 대한 요청 리디렉션&amp;#39; 옵션을 활용해 본다.우선 새로운 s3 버킷을 만든다.어떠한 이름으로 생성해도 무관하나, 나는 식별을 위해 버킷명을 도메인명으로 설정했다.생성된 버킷명을 클릭하여 설정 화면으로 이동한 뒤 [속성]을 클릭한다.스크롤을 조금 내려 &amp;#39;정적 웹 사이트 호스팅&amp;#39; 섹션에서 [편집]을 클릭한다.아래와 같이 정적 웹 사이트 호스팅을 설정한다.chanyeongpark.com 접속시 https 도메인으로 접속 되어야 하기 때문에&amp;#39;프로토콜&amp;#39;은 https를 선택한다.cloudfront 연동을 위한 s3 버킷 생성은 모두 끝났다.여기서 cloudfront 와 연동시 필요한 정보는 endpoint 다.cloudfront 연동에 앞서https://us-east-1.console.aws.amazon.com/cloudfront/v4/home?region=ap-northeast-2route53와 s3 버킷 연동만으로 리다이렉트 가능하다면 얼마나 좋을까.아쉽게도 s3 정적호스팅 endpoint는 https 엔드포인트 url을 제공하지 않는다.내가 운영하는 사이트는 https를 기본으로 사용하고 있기 때문에 인증서가 적용된 https 주소가 필요한 상황.그래서 cloudfront 추가 연동이 필요하다.그 전에, cloudfront 사용을 위해선 SSL 인증서가 미리 준비되어 있어야 하는데,다행스럽게도, AWS는 리소스 사용을 위해 필요한 인증서를 무료로 제공해주고 있다.AWS certificate manager(ACM)에서 인증서 발급https://us-east-1.console.aws.amazon.com/acm/home?region=us-east-1#/certificates/requestAWS certificate manager(ACM)에서 인증서를 발급받아 보자.주의 할 점은 &amp;#39;버지니아 북부(us-east-1)&amp;#39; 에서 발급받은 인증서만 cloudfront 에 적용 가능하니 반드시 리전부터 변경하고 보자.&amp;#39;퍼블릭 인증서 요청&amp;#39;을 선택한 뒤 아래 [다음] 버튼을 클릭한다.접속할 루트도메인 주소를 입력하고 하단 [요청]을 클릭하여 발급 요청한다.약 5분정도 기다리니 인증서가 성공적으로 발급 되었다.cloudfront 생성 및 s3 연동https://us-east-1.console.aws.amazon.com/cloudfront/v4/home?region=ap-northeast-2#/distributionscloudfront 에서 새로운 &amp;#39;배포&amp;#39;를 생성하여 s3와 연동한다.화면 상단 [배포 생성]을 클릭하여 cloudfront를 생성한다.생성화면의 &amp;#39;Choose origin domain&amp;#39;을 클릭하여, 앞서 생성한 s3 엔드포인트를 선택한다.&amp;#39;뷰어 프로토콜 정책&amp;#39; 중 &amp;#39;Redirect HTTP to HTTPS&amp;#39;를 클릭한다. 엔드포인트 접속시 http와 https 둘다 접속을 허용하지만,http 접속 시에는 https 로 강제 리다이렉트 시키기 위함이다.&amp;#39;대체 도메인 이름(cname)&amp;#39;에 루트 도메인(chanyeongpark.com)을 입력하고앞서 생성한 SSL 인증서를 선택한 다음 [배포 생성]을 클릭하여 s3와의 연동을 완료한다.route53 에서 루트도메인 레코드에 cloudfront 연결https://us-east-1.console.aws.amazon.com/route53/v2/home?region=ap-northeast-2#Dashboard드디어 마지막 단계!route53 에서 루트도메인에 방금 만든 cloudfront 배포 엔드포인트를 연동한다.dns 호스팅 영역을 선택한 다음 [레코드 생성]을 선택한다.위와 같이 [별칭]을 선택하면 추가 선택 항목이 노출 되는데&amp;#39;트래픽 라우팅 대상&amp;#39; 에서 &amp;#39;CloudFront 배포에 대한 별칭&amp;#39;,&amp;#39;배포 선택&amp;#39; 에서 방금 만든 cloudfront 엔드포인트를 선택한 다음 [레코드 생성]을 클릭하여 모든 과정을 마무리 한다.마지막으로, 브라우저 창에서 루트도메인 접속시 &amp;#39;www.도메인&amp;#39;으로 리다이렉션이 잘 되는지 확인한다.</description>
<category>AWS</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/aws/4</guid>
<comments>https://note.chanyeongpark.com/posts/aws/4</comments>
<pubDate>Mon, 25 Mar 2024 01:40:46 +0900</pubDate>
</item>
<item>
<title>[mariadb] mariadb table schema (테이블 명세) 출력 쿼리</title>
<link>https://note.chanyeongpark.com/posts/linux/24</link>
<description>mysql table 명세서를 아래 쿼리를 통해 출력할 수 있다.명세서 작성시 유용하게 활용할 수 있다.table schema 추출아래 스키마명, 테이블명을 적절히 수정하여 사용한다.SELECT t1.table_name, t1.table_comment, column_name, data_type, column_type, column_key, is_nullable, column_default, extra, column_commentFROM (SELECT   table_name, table_comment  FROM   information_schema.TABLES WHERE table_schema=&amp;#39;스키마명&amp;#39; AND table_name=&amp;#39;테이블명&amp;#39;) t1, (SELECT   table_name, column_name, data_type, column_type, column_key, is_nullable, column_default, extra, column_comment, ordinal_position  FROM   information_schema.COLUMNS WHERE table_schema=&amp;#39;스키마명&amp;#39; AND table_name=&amp;#39;테이블명&amp;#39;) t2WHERE  t1.table_name = t2.table_nameORDER BY  t1.table_name, ordinal_position;아래와 같이 결과가 출력된다.</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/24</guid>
<comments>https://note.chanyeongpark.com/posts/linux/24</comments>
<pubDate>Thu, 07 Mar 2024 15:07:58 +0900</pubDate>
</item>
<item>
<title>[centOS]  경로내 .DS_Store 파일 일괄 삭제</title>
<link>https://note.chanyeongpark.com/posts/linux/23</link>
<description>.DS_Store 파일은 맥에서 디렉토리 접근시 자동 생성되는 메타데이터를 저장하는 &amp;#39;숨김 파일&amp;#39;이다.윈도우의thumb.db 과 같은 맥락의 파일이다.아래와 같이 맥에서 생성되는 .DS_Store 파일을 일괄 삭제할 수 있다..DS_Store 일괄 삭제아래와 일괄 삭제.#find . -type f -name .DS_Store -delete</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/23</guid>
<comments>https://note.chanyeongpark.com/posts/linux/23</comments>
<pubDate>Sat, 17 Feb 2024 15:24:43 +0900</pubDate>
</item>
<item>
<title>AWS PHP sdk로  API Gateway 에 api 키 생성, 사용량계획 연결, 키 삭제</title>
<link>https://note.chanyeongpark.com/posts/aws/3</link>
<description>API Gateway에 요율 및 제한량을 api 유저별로 개별 설정해 주기 위해 api 키 생성후 사용량 계획 연결이 필요한데,php sdk를 통해 api 키 생성 및 미리 만들어 놓은 사용량계획을 연결해 주는 방법.IAM 에서 access key 와 secret key 생성아래 링크를 접속하여 IAM 에서 API에 사용할 access key 와 secret key를 미리 생성해둔다.https://console.aws.amazon.com/iam/home?region=ap-northeast-2#/security_credentials키 생성 및 사용계획 연결아래와 같이 구현 가능한데, region, 사용량계획id 등을 적절히 수정해준다.use Aws\Credentials\Credentials;use Aws\Signature\SignatureV4;use Aws\ApiGateway\ApiGatewayClient;// signature 생성$accessKey = &amp;#39;access key&amp;#39;;$secretKey = &amp;#39;srcret key&amp;#39;;// AWS 자격 증명 및 API Gateway 클라이언트 설정$credentials = new Credentials($accessKey, $secretKey);$apiGatewayClient = new ApiGatewayClient([  &amp;#39;region&amp;#39; =&amp;gt; &amp;#39;ap-northeast-2&amp;#39;,  &amp;#39;version&amp;#39; =&amp;gt; &amp;#39;latest&amp;#39;,  &amp;#39;credentials&amp;#39; =&amp;gt; $credentials,  &amp;#39;signature&amp;#39; =&amp;gt; new SignatureV4(&amp;#39;apigateway&amp;#39;, &amp;#39;ap-northeast-2&amp;#39;) // region 설정]);// API 키 생성$result = $apiGatewayClient-&amp;gt;createApiKey([  &amp;#39;name&amp;#39; =&amp;gt; &amp;#39;userskey123&amp;#39;, // 중복되지 않는 고유한 이름으로 수정  &amp;#39;enabled&amp;#39; =&amp;gt; true, // 활성화 여부  &amp;#39;description&amp;#39; =&amp;gt; &amp;#39;hello&amp;#39;, // 메모  &amp;#39;value&amp;#39; =&amp;gt; &amp;#39;codehere&amp;#39;.rand(), // 코드를 수동 생성하려면 입력.]);$apiKeyId = $result[&amp;#39;id&amp;#39;]; // 성공 결과 값에서 생성된 key의 id를 가져옴// 사용량계획 연결$result = $apiGatewayClient-&amp;gt;createUsagePlanKey([  &amp;#39;keyId&amp;#39; =&amp;gt; $apiKeyId, // 위에서 생성한 키 id  &amp;#39;usagePlanId&amp;#39; =&amp;gt; &amp;#39;plan id hear&amp;#39;, // 미리 만들어 놓은 사용량 계획 ID  &amp;#39;keyType&amp;#39; =&amp;gt; &amp;#39;API_KEY&amp;#39;]);키 삭제키 생성시 DB에 별도 저장한 키 id로 삭제를 수행한다.use Aws\Credentials\Credentials;use Aws\Signature\SignatureV4;use Aws\ApiGateway\ApiGatewayClient;// signature 생성$accessKey = &amp;#39;access key&amp;#39;;$secretKey = &amp;#39;srcret key&amp;#39;;// AWS 자격 증명 및 API Gateway 클라이언트 설정$credentials = new Credentials($accessKey, $secretKey);$apiGatewayClient = new ApiGatewayClient([  &amp;#39;region&amp;#39; =&amp;gt; &amp;#39;ap-northeast-2&amp;#39;,  &amp;#39;version&amp;#39; =&amp;gt; &amp;#39;latest&amp;#39;,  &amp;#39;credentials&amp;#39; =&amp;gt; $credentials,  &amp;#39;signature&amp;#39; =&amp;gt; new SignatureV4(&amp;#39;apigateway&amp;#39;, &amp;#39;ap-northeast-2&amp;#39;) // region 설정]);// 삭제할 API 키의 ID$apiKeyIdToDelete = &amp;#39;DB에 별도 보관한 키 ID 입력&amp;#39;;// API 키 삭제$apiGatewayClient-&amp;gt;deleteApiKey([  &amp;#39;apiKey&amp;#39; =&amp;gt; $apiKeyIdToDelete,]);</description>
<category>AWS</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/aws/3</guid>
<comments>https://note.chanyeongpark.com/posts/aws/3</comments>
<pubDate>Mon, 18 Dec 2023 23:38:06 +0900</pubDate>
</item>
<item>
<title>[jQuery] Lazy 로 이미지 리소스 트래픽 절약하기</title>
<link>https://note.chanyeongpark.com/posts/jquery/24</link>
<description>이미지가 리소스가 많은 웹페이지 로드시 과도한 트래픽이 발생하게 되는데,lazy 는 화면에 노출된 이미지 리소스를 그때그때 로드하여 출력해 주는 플러그인이다.Plugin includecdn 서비스나 본 글 하단에 첨부된 js파일을 다운로드 하여 lazy 적용을 원하는 웹페이지 상단에 인클루드 한다.&amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;jquery.lazy.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;img 태그 수정lazy가 적용될 &amp;lt;img&amp;gt; 요소를 아래와 같이 수정한 다음, plugin 을 적용한다.&amp;lt;HTML&amp;gt;1.src 어트리뷰트를 data-src 로 변경2. class=&amp;quot;lazy&amp;quot; 추가&amp;lt;img data-src=&amp;quot;path/to/image.jpg&amp;quot; class=&amp;quot;lazy&amp;quot; /&amp;gt;&amp;lt;jQuery&amp;gt;.lazy 가 부여된 이미지 리소스에 대해 lazy 적용$(function() {  $(&amp;#39;.lazy&amp;#39;).Lazy();});상세 옵션 설정lazy는 커스터마이징이나 디버깅 할 수 있는 몇가지의 옵션을 제공한다.( 전체 옵션 확인 :http://jquery.eisbehr.de/lazy/#configuration )$(function() {  $(&amp;#39;.lazy&amp;#39;).Lazy({    &amp;#39;thresold&amp;#39; : 100, // 이미지가 화면에 100px 만큼 보인 시점에 등장하도록 설정    &amp;#39;effect&amp;#39; : &amp;#39;fadeIn&amp;#39;, // fade 등장효과 적용 (기본값: show)    &amp;#39;effectTime&amp;#39; : 500, // fade 적용시 duration 시간    &amp;#39;onError&amp;#39; : function(element) { // img 로드 실패시 callback 함수      console.log(&amp;#39;error loading &amp;#39; + element.data(&amp;#39;src&amp;#39;));    }  });});</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/24</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/24</comments>
<pubDate>Wed, 18 Oct 2023 10:58:57 +0900</pubDate>
</item>
<item>
<title>[CSS] backdrop-filter 로 겹침 효과 주기</title>
<link>https://note.chanyeongpark.com/posts/html_css/13</link>
<description>한때 음악 어플의 앨범 커버이미지 ui에서 자주 볼 수 있었던 흐림 효과를 CSS로 구현할 수 있다.backdrop-filter 를 통해 구현할 수 있는데, IE가 지원 중단 되면서 부터 비교적 자유롭게 사용할 수 있게 되었다.사용법은 아래와 같다.blurbackdrop-filter 중에 내가 가장 자주 사용하는 속성은 blur.backdrop-filter: blur(10px);위와 같이 적용해 주면 되는데,다만, 부모 엘리먼트에 이미 backgrop-filter 가 적용되어 있다면, 자식 요소에서는 적용되지 않는다는 점을 주의하자.invert / sepia등 다양한 속성제공되는 다양한 속성 중 inver와 sepia를 비롯해 다양한 속성을 제공한다.예시는 아래와 같다.backdrop-filter: invert(80%);backdrop-filter: sepia(90%);아래는 Mozilla 가이드 페이지에서 제공하는 속성 설명이다.</description>
<category>HTML / CSS</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/html_css/13</guid>
<comments>https://note.chanyeongpark.com/posts/html_css/13</comments>
<pubDate>Wed, 11 Oct 2023 09:55:18 +0900</pubDate>
</item>
<item>
<title>AWS S3 리소스에 대한 CORS(크로스도메인) 설정 방법</title>
<link>https://note.chanyeongpark.com/posts/aws/2</link>
<description>S3에 저장된 이미지 리소스를 외부 사이트 웹페이지 등에서 사용 할 때웹페이지의 도메인과 S3 endpoint의 도메인이 달라 크로스도메인(CORS) 오류가 발생하여 리소스가 로드되지 않는 경우S3 콘솔에서 CORS 를 설정하여 특정 도메인의 크로스도메인 접근을 허용할 수 있다.AWS S3 버킷 관리 콘솔AWS - S3 - 버킷 선택 - 권한 -CORS(Cross-origin 리소스 공유)로 이동하여 [편집] 버튼을 클릭하여 관리 팝업을 연 다음 아래와 같 이 입력한다.CORS 설정 (JSON)AWS S3 에서는 JSON으로 CORS 설정이 가능하여 아래 구문을 복사한 뒤적절히 수정하여 적용한다.[  {    &amp;quot;AllowedHeaders&amp;quot;: [      &amp;quot;*&amp;quot;    ],    &amp;quot;AllowedMethods&amp;quot;: [      &amp;quot;GET&amp;quot;,      &amp;quot;PUT&amp;quot;,      &amp;quot;POST&amp;quot;,      &amp;quot;DELETE&amp;quot;    ],    &amp;quot;AllowedOrigins&amp;quot;: [      &amp;quot;https://허용할도메인1.com&amp;quot;,      &amp;quot;http://허용할도메인2.com&amp;quot;    ],    &amp;quot;ExposeHeaders&amp;quot;: []  }]CORS 설정 (XML)대부분의 Object Storage 서비스는 S3 CORS 설정 방법과 동일하나,XML 구문만 지원하는 서비스도 간혹 있는데,XML의 경우 아래와 같이 설정한다.&amp;lt;CORSConfiguration&amp;gt;&amp;lt;CORSRule&amp;gt; &amp;lt;AllowedOrigin&amp;gt;https://허용할도메인1.com&amp;lt;/AllowedOrigin&amp;gt; &amp;lt;AllowedMethod&amp;gt;PUT&amp;lt;/AllowedMethod&amp;gt; &amp;lt;AllowedMethod&amp;gt;POST&amp;lt;/AllowedMethod&amp;gt; &amp;lt;AllowedMethod&amp;gt;DELETE&amp;lt;/AllowedMethod&amp;gt; &amp;lt;AllowedHeader&amp;gt;*&amp;lt;/AllowedHeader&amp;gt;&amp;lt;/CORSRule&amp;gt;&amp;lt;CORSRule&amp;gt; &amp;lt;AllowedOrigin&amp;gt;http://허용할도메인2.com&amp;lt;/AllowedOrigin&amp;gt; &amp;lt;AllowedMethod&amp;gt;PUT&amp;lt;/AllowedMethod&amp;gt; &amp;lt;AllowedMethod&amp;gt;POST&amp;lt;/AllowedMethod&amp;gt; &amp;lt;AllowedMethod&amp;gt;DELETE&amp;lt;/AllowedMethod&amp;gt; &amp;lt;AllowedHeader&amp;gt;*&amp;lt;/AllowedHeader&amp;gt;&amp;lt;/CORSRule&amp;gt;&amp;lt;CORSRule&amp;gt; &amp;lt;AllowedOrigin&amp;gt;*&amp;lt;/AllowedOrigin&amp;gt; &amp;lt;AllowedMethod&amp;gt;GET&amp;lt;/AllowedMethod&amp;gt;&amp;lt;/CORSRule&amp;gt;&amp;lt;/CORSConfiguration&amp;gt;</description>
<category>AWS</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/aws/2</guid>
<comments>https://note.chanyeongpark.com/posts/aws/2</comments>
<pubDate>Thu, 05 Oct 2023 16:51:24 +0900</pubDate>
</item>
<item>
<title>[HTML] textarea placeholder 에서 줄바꿈하기</title>
<link>https://note.chanyeongpark.com/posts/html_css/12</link>
<description>textarea 에서 placeholder 입력시 \n 혹은 \r\n 로 줄바꿈을 할 수 없는데,아래와 같이 엔터티를 입력하여 줄바꿈 가능하다.&amp;lt;textarea placeholder=&amp;quot;첫 번째 줄&amp;amp;#10;두 번째 줄&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;줄바꿈 엔터티 :&amp;amp;#10;아래는 줄바꿈 placeholder 가 적용된 textarea.</description>
<category>HTML / CSS</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/html_css/12</guid>
<comments>https://note.chanyeongpark.com/posts/html_css/12</comments>
<pubDate>Wed, 13 Sep 2023 17:10:57 +0900</pubDate>
</item>
<item>
<title>[CSS] CSS만으로 scrollbar 디자인하기</title>
<link>https://note.chanyeongpark.com/posts/html_css/10</link>
<description>최근 익스플로러가 사용 중단 조치 되면서 스크롤바 디자인이 좀 더 자유로워졌다.아래 CSS를 통해 간단히 웹킷 브라우저에서 스크롤바 디자인을 변경할 수 있다.CSS아래 코드를 통해 스크롤바 디자인이 가능하다..scroll::-webkit-scrollbar {width: 10px}.scroll::-webkit-scrollbar-thumb {background-color: #b4b4b4; border-radius: 10px; background-clip: padding-box}.scroll::-webkit-scrollbar-track {background-color: #f1f1f1; border-radius: 10px; }위 코드에서 .scroll 은 스크롤이 적용되는 엘리먼트이며,::-webkit-scrollbar는 스크롤바 전체 영역,::-webkit-scrollbar-thumb는 스크롤 바,::-webkit-scrollbar-track 는 트랙의 스타일을 지정하면 된다.예시는 아래와 같다.</description>
<category>HTML / CSS</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/html_css/10</guid>
<comments>https://note.chanyeongpark.com/posts/html_css/10</comments>
<pubDate>Wed, 05 Jul 2023 20:06:59 +0900</pubDate>
</item>
<item>
<title>[jQuery] jQuery UI autocomplete 적용하기</title>
<link>https://note.chanyeongpark.com/posts/jquery/23</link>
<description>jQuery UI에 기본 탑재된 자동완성 기능 (autocomplete) 샘플 코드.배열을 미리 선언하여 사용하는 방법과 ajax 방식으로 배열을 가져와 구동하는 방법으로 사용할 수 있는데아래 예시는 배열을 미리 선언한 뒤 사용하는 방식이다.자동완성 배열 선언searchSource = [ { &amp;#39;label&amp;#39; : &amp;#39;서울&amp;#39;, &amp;#39;value&amp;#39; : &amp;#39;seoul&amp;#39; }. { &amp;#39;label&amp;#39; : &amp;#39;대전&amp;#39;, &amp;#39;value&amp;#39; : &amp;#39;daejun&amp;#39; }]자동완성 적용위에서 선언된 배열을 자동완성으로 적용하는 예제이며,자동완성을 선택하는 경우 위 배열 중 &amp;#39;value&amp;#39;는 input의 value가 되며,&amp;#39;label&amp;#39;은 자동완성에서 노출될 항목명이 된다.&amp;lt;HTML&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;address&amp;quot; autocomplete=&amp;quot;off&amp;quot;&amp;gt;&amp;lt;jQuery&amp;gt;$(&amp;#39;input[name=&amp;quot;address&amp;quot;]&amp;#39;).autocomplete({  &amp;#39;source&amp;#39; : searchSource, // 위에서 선언한 배열  &amp;#39;minLength&amp;#39; : 0, // 최소 몇글자 입력시 자농완성 노출할 것인지  &amp;#39;autoFocus&amp;#39; : false, // 자동으로 첫번째 항목에 focus를 위치시킬 것인지  &amp;#39;position&amp;#39; : { my : &amp;#39;right top&amp;#39;, at : &amp;#39;right bottom&amp;#39; }, // 자동완성 창의 노출 위치  &amp;#39;delay&amp;#39; : 0, // 노출 딜레이 시간  &amp;#39;z-index&amp;#39; : 999, // z-index  &amp;#39;classes&amp;#39; : {}, // 자동완성 기본 css class 를 custom class로 대입하는 옵션.  &amp;#39;select&amp;#39; : function(event, ui) {}, // 항목 선택시 수행할 작업  &amp;#39;focus&amp;#39; : function(event, ui) {}, // 항목 focus시 수행할 작업  &amp;#39;close&amp;#39; : function(event) {} // 닫는 경우 수행할 작업}).on({  &amp;#39;focus&amp;#39; : function() {    $(this).autocomplete(&amp;quot;search&amp;quot;, &amp;quot;&amp;quot;); // input에 포커싱 되는 순간 자동완성이 열리게 하는 코드  }});autocomplete가 적용된 ui는 아래 예시와 같다.</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/23</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/23</comments>
<pubDate>Wed, 05 Jul 2023 19:44:10 +0900</pubDate>
</item>
<item>
<title>[PHP] 배경색(HEX 코드) 판별하여 적합한 글자색(흰색/검정색) 반환</title>
<link>https://note.chanyeongpark.com/posts/php/8</link>
<description>Element에 배경색과 배경색 위에 얹혀질 글자 색을 지정하는 경우배경색이 밝은지 어두운지 판별하여 횐색/검정 글자중 적합한 색을 반환해 주는 함수.코드에 HEX코드를 넣어주면 결과 값으로 black 혹은 white를 반환해준다.배경색에 따라 white / black 반환아래와 같이 함수에 HEX값 (배경색)을 넣어주면 white / black을 반환해 준다.// 글자색 반환 함수function get_text_color($hex) {    $rgb = sscanf($hex, &amp;quot;#%2x%2x%2x&amp;quot;);    $brightness = (($rgb[0] * 299) + ($rgb[1] * 587) + ($rgb[2] * 114)) / 1000;    return ($brightness &amp;gt; 125) ? &amp;#39;black&amp;#39; : &amp;#39;white&amp;#39;;}// 글자색 반환 실행get_text_color(&amp;#39;#000000&amp;#39;); // 결과 값 : &amp;#39;white&amp;#39; 반환위 함수는 아래와 같이 활용할 수 있다.</description>
<category>PHP</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/php/8</guid>
<comments>https://note.chanyeongpark.com/posts/php/8</comments>
<pubDate>Tue, 27 Jun 2023 09:43:45 +0900</pubDate>
</item>
<item>
<title>[centOS] 버퍼메모리 초기화 방법</title>
<link>https://note.chanyeongpark.com/posts/linux/22</link>
<description>프로그램의 과부하 혹은 메모리 누수로 인해 버퍼메모리가 과도하게 발생한 경우아래와 같이 강제로 메모리를 초기화 할 수 있다.퍼버메모리 초기화아래와 같이 버퍼 메모리를 초기화 할 수 있다.#echo 3 &amp;gt; /proc/sys/vm/drop_cache</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/22</guid>
<comments>https://note.chanyeongpark.com/posts/linux/22</comments>
<pubDate>Wed, 21 Jun 2023 14:47:24 +0900</pubDate>
</item>
<item>
<title>[centOS] 현재 FTP/SSH 접속해있는 접속자 ip 확인하기</title>
<link>https://note.chanyeongpark.com/posts/linux/21</link>
<description>현재 FTP와 SSH에 접속해있는 접속 ip를 확인할 수 있다.현재 FTP/SSH 접속 ip확인아래와 같이확인#last | grep logged | awk &amp;#39;{print $1,$3}&amp;#39; | sort | uniq -c | sort -n | awk &amp;#39;{print $2,$3}&amp;#39;</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/21</guid>
<comments>https://note.chanyeongpark.com/posts/linux/21</comments>
<pubDate>Wed, 21 Jun 2023 14:46:09 +0900</pubDate>
</item>
<item>
<title>[centOS] hosts.allow 와 hosts.deny 설정으로 서버 접근 ip 차단</title>
<link>https://note.chanyeongpark.com/posts/linux/20</link>
<description>iptable에서 아이피 접근을 차단할 수 있지만,hosts.allow 와 hosts.deny 파일 설정을 통해 트래픽 유형별로 세분화하여 아이피 차단/허용 처리 할 수 있다.hosts.allow 로 아이피 차단아이피 접근 차단 방법을 안내한다.# vi /etc/hosts.deny설정 파일을 vi로 연다....ALL: ALL위와 같이 모든 아이피를 차단할 수 있으며, 이 경우 허용하고자 하는 특정 아이피만 hosts.allow 에서 열어줘야 한다.입력 방법은 아래 참고....ALL: 111.222.333.444, 222.333.444.555위와 같이 차단할 아이피를 지정할 수 있다. 여러 아이피인 경우 콤마(,)로 입력한다....sendmail: 111.222.333.444, 222.333.444.555sendmail: ALL위와 같이 sendmail 에 대한 접근 차단을 할 수도 있다.hosts.allow 로 아이피 허용아이피 접근 허용 방법을 안내한다.# vi /etc/hosts.allow설정 파일을 vi로 연다....ALL: 111.222.333.444, 222.333.444.555위와 같이 허용할 아이피를 지정할 수 있다. 여러 아이피인 경우 콤마(,)로 입력한다....sendmail: 111.222.333.444, 222.333.444.555위와 같이 sendmail 에 대한 접근 허용을 할 수도 있다.</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/20</guid>
<comments>https://note.chanyeongpark.com/posts/linux/20</comments>
<pubDate>Wed, 21 Jun 2023 10:28:28 +0900</pubDate>
</item>
<item>
<title>[centOS] 포트별 접속자 수를 출력하는 sh 스크립트 파일 작성</title>
<link>https://note.chanyeongpark.com/posts/linux/19</link>
<description>현재 포트별 접속자 수를 출력하는 스크립트.스크립트 파일 작성vi로 스크립트 파일을 작성한다.# vi /var/log/port_stat.sh파일 내용에 아래와 같이 입력한다.#!/bin/bashports=(&amp;quot;443&amp;quot; &amp;quot;80&amp;quot; &amp;quot;21000&amp;quot; &amp;quot;20000&amp;quot; &amp;quot;3306&amp;quot;)for port in &amp;quot;${ports[@]}&amp;quot;; do count=$(netstat -an | grep &amp;quot;:${port}&amp;quot; | wc -l) echo &amp;quot;Port ${port} =&amp;gt; ${count}&amp;quot;done위 스크립트를 실행하면, 아래와 같이 포트별 접속자 수가 출력된다.# /var/log/port_stat.sh</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/19</guid>
<comments>https://note.chanyeongpark.com/posts/linux/19</comments>
<pubDate>Thu, 08 Jun 2023 23:15:43 +0900</pubDate>
</item>
<item>
<title>[centOS] 아파치 access_log 에서 특정 시간동안의 아이피별 접속 횟수 추출하기</title>
<link>https://note.chanyeongpark.com/posts/linux/18</link>
<description>아파치의 access_log 파일 내용을 활용하여 특정 기간동안 아이피별로 접속 횟수를 추출해주는 스크립트 파일.오늘동안의 아이피별 접속 횟수 출력vi로 sh 파일을 생성한다.# vi /var/log/today_log.sh파일 내용에 아래와 같이 입력한다.#!/bin/bash# 로케일을 영어(미국)으로 변경export LC_TIME=&amp;quot;en_US.UTF-8&amp;quot;LOG_FILES=( &amp;quot;/var/log/httpd/access_log&amp;quot; #&amp;quot;/home/zigger.net/access_log&amp;quot; #&amp;quot;/home/bboggi.com/access_log&amp;quot; #&amp;quot;/home/note.chanyeongpark.com/access_log&amp;quot;)DATE=$(date +&amp;quot;%d/%b/%Y&amp;quot;)for LOG_FILE in &amp;quot;${LOG_FILES[@]}&amp;quot;; do output=$(grep &amp;quot;${DATE}&amp;quot; &amp;quot;${LOG_FILE}&amp;quot; | grep -Ev &amp;#39;(\.js|\.css|\.woff|\.jpg|\.jpeg|\.gif|\.png|\.bmp|\.txt)&amp;#39; | awk &amp;#39;{ip_count[$1]++} END {for (ip in ip_count) {print ip, ip_count[ip]}}&amp;#39; | sort -k2,2n) echo &amp;quot;[ Log File: ${LOG_FILE} ]&amp;quot; echo &amp;quot;$output&amp;quot; echoecho ${DATE}done위 스크립트중LOG_FILES배열은 추출할 다수의 로그 파일 경로를 지정하며,grep -Ev &amp;#39;(\.js|\.css|\.woff|\.jpg|\.jpeg|\.gif|\.png|\.bmp|\.txt)&amp;#39; 는 js, css, jpg 등 리소스 파일은 제외하고 순수한 웹페이지 파일 접속 아이피만을 추출한다.위 스크립트를 실행하면, 아래와 같이 아이피별 접속 횟수가 출력된다.# /var/log/today_log.sh현재 &amp;#39;시&amp;#39; 기준 아이피별 접속 횟수 출력현재 &amp;#39;시&amp;#39;를 기준으로 아이피별 접속 횟수는 아래와 같이 작성한다.#!/bin/bash# 로케일을 영어(미국)으로 변경export LC_TIME=&amp;quot;en_US.UTF-8&amp;quot;# 로그 파일 경로 설정LOG_FILES=( &amp;quot;/var/log/httpd/access_log&amp;quot;)# 현재 시간을 구해서 DATE 변수에 저장 (한 시간 전으로 설정)DATE=$(date +&amp;quot;%d/%b/%Y:%H&amp;quot;)for LOG_FILE in &amp;quot;${LOG_FILES[@]}&amp;quot;; do # 해당 로그 파일에서 최근 한 시간 동안의 접속 기록을 필터링하고 IP 주소별로 카운트 output=$(grep &amp;quot;${DATE}&amp;quot; &amp;quot;${LOG_FILE}&amp;quot; | grep -Ev &amp;#39;(\.js|\.css|\.woff|\.jpg|\.jpeg|\.gif|\.png|\.bmp|\.txt)&amp;#39; | awk &amp;#39;{ip_count[$1]++} END {for (ip in ip_count) {print ip, ip_count[ip]}}&amp;#39; | sort -k2,2n) echo &amp;quot;[ Log File: ${LOG_FILE} ]&amp;quot; echo &amp;quot;$output&amp;quot; echoecho ${DATE}done</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/18</guid>
<comments>https://note.chanyeongpark.com/posts/linux/18</comments>
<pubDate>Thu, 08 Jun 2023 23:07:06 +0900</pubDate>
</item>
<item>
<title>[centOS] CentOS 7 에서 기본적인 iptables 설정 방법</title>
<link>https://note.chanyeongpark.com/posts/linux/17</link>
<description>centos7은 firewalld와 iptable이 함께 구동 되는데,나의 경우 firewalld가 익숙하지 않기 때문에 firewalld를 비활성화 한 뒤 iptable 위주로 사용한다.서버 앞단에 방화벽이 없는 경우 iptable 설정이 필수이기 때문에 기본적인 설정 방법을 정리해 본다.firewalld 사용을 원치 않는 경우 비활성화 하기아래 명령어로 firewalld를 종료 및 비활성화 한다.# systemctl stop firewalld# systemctl mask firewalldiptables-services 설치iptable-services 가 설치되어 있지 않은 경우 iptables 데몬 시작시&amp;#39;Failed to start iptables.service: Unit not found.&amp;#39;와 같은 에러가 발생한다.아래와 같이 iptables-services 를 설치한다.#yum install iptables-servicesiptables 정책 확인아래와 같이 현재 설정되어 있는 iptable 정책 내용을 확인한다.# iptables -nL모든 포트 접속을 차단한 뒤 특정 포트만 접속 허용보안을 위해 서버에 들어오는 모든 포트를 기본적으로 차단하고 특정 필요한 포트만 개방시키는 정책을 설정한다.먼저 설정되는 정책이 우선 순위를 갖게 되므로 허용할 포트를 먼저 설정한 뒤 모든 포트를 차단하는 정책을 마지막에 설정해야 한다.차단 정책이 먼저 설정되는 경우 SSH 접속이 불가하니 주의해야 한다.가장 먼저, 현재 등록되어 있는 정책을 모두 초기화 시킨다.# iptables -Foutput tcp/udp 포트는 모두 개방시킨다.# iptables -A OUTPUT -p tcp -j ACCEPT# iptables -A OUTPUT -p udp -j ACCEPT한번 연결된 트래픽에 대한 응답을 지속 시키기 위해 아래와 같이 정책을 추가한다.아래 정책이 없는 경우 pdo로 database를 연결하는 웹앱에서 문제가 발생할 수 있다.# iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT그 다음, 필요한 포트를 아래와 같이 개방 시킨다.# iptables -A INPUT -p tcp --dport 22 -j ACCEPT아래와 같이 범위로 포트를 지정할 수도 있다.# iptables -A INPUT -p tcp --match multiport --dports 5000:5050 -j ACCEPT필요 포트를 모두 개방 하였다면,마지막으로 나머지 포트를 모두 차단한다.# iptables -A INPUT -j DROP등록된 정책의 우선순위 변경아래와 같이 나중에 등록한 정책의 우선순위를 높일 수 있다.원하는 우선순위를 숫자로 입력하여 설정한다.# iptables -I INPUT 1 -p tcp --dport 8080 -j ACCEPT등록된 정책 삭제등록된 정책 중 원치않는 정책을 삭제할 수 있다.우선순위의 숫자로 지정 하거나, 포트 번호를 명시하여 지정할 수 있다.# iptables -D INPUT 1#iptables -D INPUT -p tcp --dport 8080 -j ACCEPT정책 저장 및 복구iptable 사용시 주의해야 할 점은, 데몬을 restart하는 경우 정책이 모두 초기화 된다는 점이다.(이를 보완하기 위한 데몬이 존재하기는 하지만)지금까지 설정한 정책을 시스템에 저장한 뒤 원할 때 마다 복구시킬 수 있는 방법이 있다.먼저, iptable 정책 설정이 마무리 되었다면 아래와 같이 정책을 저장한다.# service iptables saveor# iptables-save &amp;gt; /etc/sysconfig/iptables데몬 재시작 후 정책이 초기화 되었다면 아래와 같이 저장된 정책을 다시 복구시킨다.# iptables-restore &amp;lt; /etc/sysconfig/iptables iptable을 통해 접속 ip 차단 시키기iptable에서 특정 input ip를 접속 차단시킬 수 있는데, 정책 등록시 아이피 대역으로도 설정할 수 있다.아래와 같이 ip를 지정하여 접속을 차단시킨다.(이때 아이피 차단을 가장 우선순위로 하기 위해 숫자 1을 함께 지정한다.)# iptables -A INPUT 1 -s 111.222.333.444 -j DROP[ ip대역으로 차단하는 경우 - 111.222.333.xxx ]# iptables -A INPUT 1 -s 111.222.333.0/24 -j DROP[ ip대역으로 차단하는 경우 - 111.222.xxx.xxx ]# iptables -A INPUT 1 -s 111.222.0.0/16 -j DROP[ ip대역으로 차단하는 경우 - 111.xxx.xxx.xxx ]# iptables -A INPUT 1 -s 111.0.0.0/8 -j DROPvi 로 iptable 관리하기앞서 명령어를 통한 iptable 설정방법 외에도 아래와 같이 vi 로도 설정할 수 있다.iptable에 등록된 정보가 많은 경우 vi를 통해 손쉽게 관리 가능하다.# vi /etc/sysconfig/iptables</description>
<category>Linux</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/linux/17</guid>
<comments>https://note.chanyeongpark.com/posts/linux/17</comments>
<pubDate>Thu, 08 Jun 2023 22:26:18 +0900</pubDate>
</item>
<item>
<title>[PHP] sms 본문 내용 byte 계산 (멀티바이트로 계산하기 - 한글 2byte)</title>
<link>https://note.chanyeongpark.com/posts/php/7</link>
<description>SMS의 내용을 byte로 계산할 때 일반적으로 한글, 특수문자는 2byte, 영어, 숫자 줄바꿈등은 1byte로 계산한다.한글을 php나, javascript에서 UTF-8의 byte를 계산하는 경우 3byte로 계산하는데, sms 서비스의 경우 한글을 2byte로 계산해야 한다.mb_strwidth() 함수를 사용한 byte 계산아래와 같이 멀티바이트 (string width)로 계산하면 한글을 2byte로 계산할 수 있다.function getByteLength($str) {    $strlen = mb_strwidth(str_replace(&amp;quot;\r\n&amp;quot;, &amp;quot;\n&amp;quot;, $str), &amp;#39;UTF-8&amp;#39;);    return $strlen;}위 코드 중str_replace(&amp;quot;\r\n&amp;quot;, &amp;quot;\n&amp;quot;, $str) 코드는리눅스의 경우 줄바꿈을 \r\n 으로 처리하기 때문에 2byte가 되는데,이를 윈도우 기준인 \n으로 치환하여 1byte로 계산 하도록 한다.</description>
<category>PHP</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/php/7</guid>
<comments>https://note.chanyeongpark.com/posts/php/7</comments>
<pubDate>Thu, 08 Jun 2023 22:17:11 +0900</pubDate>
</item>
<item>
<title>[Javascript] SMS 본문 내용 byte 수 계산하는 함수</title>
<link>https://note.chanyeongpark.com/posts/jquery/22</link>
<description>SMS문자 본문 내용의 byte를 계산하는 간단한 코드.ASCII 코드가 128이상인 경우(한글, 한문, 특수기호 등) 2byte로 계산하고 그 외의 문자 (영어, 숫자, 기본기호 등)는1byte로 계산한다.Javascript// 내용 byte 체크function getByteLength(str) {  var byteLen = 0;  for(var i = 0; i &amp;lt; str.length; i++) {    var charCode = str.charCodeAt(i);    if (charCode &amp;gt; 128) {      byteLen += 2;    } else {      byteLen ++;    }  }  return byteLen;}</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/22</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/22</comments>
<pubDate>Mon, 17 Apr 2023 19:44:26 +0900</pubDate>
</item>
<item>
<title>[jQuery] 마우스 드래그 금지 &amp; 우클릭 금지</title>
<link>https://note.chanyeongpark.com/posts/jquery/21</link>
<description>마우스 드래그 및 우클릭 금지하는 jquery 코드.event로 간단하게 처리 가능하다.jQuery&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;$(document).ready(function(){  //우클릭 금지 $(document).bind(&amp;#39;contextmenu&amp;#39;, function(e){  return false; }); //드래그 금지 $(&amp;#39;*&amp;#39;).bind(&amp;#39;selectstart&amp;#39;, function(e){  return false; });});&amp;lt;/script&amp;gt;</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/21</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/21</comments>
<pubDate>Wed, 29 Mar 2023 11:03:02 +0900</pubDate>
</item>
<item>
<title>[PHP] microtime과 랜덤 문자를 조합하여 중복되지 않는 PK 문자 만들기</title>
<link>https://note.chanyeongpark.com/posts/php/6</link>
<description>microtime() 과 랜덤 글자 조합하여 중복되지 않는 Primary Key 문자를 생성하는 함수.함수의 인자 값으로 원하는 길이를 넘기면 랜덤 문자열과 microtime()을 조합 후, 다시str_shuffle()로 무작위로 섞은결과 문자열을 return 해준다.($length는 최소 30자 이상 설정 가능)Primary Key 생성 함수function make_random_char($length = 30)  {    $length = ($length &amp;lt; 30) ? 30 : $length;    $length = $length - 19;    $characters = &amp;#39;0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;#39;;    $randomString = &amp;#39;&amp;#39;;    $max = strlen($characters) - 1;    for ($i = 0; $i &amp;lt; $length; $i++) {      $randomString .= $characters[mt_rand(0, $max)];    }    $microtime = str_replace(array(&amp;#39; &amp;#39;, &amp;#39;.&amp;#39;), array(&amp;#39;&amp;#39;, &amp;#39;&amp;#39;), microtime());       return str_shuffle($randomString.$microtime); // 문자열을 다시 무작위로 섞음  }결과 예시4q344nM250001GS69e2y2X61198mT1</description>
<category>PHP</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/php/6</guid>
<comments>https://note.chanyeongpark.com/posts/php/6</comments>
<pubDate>Thu, 16 Mar 2023 17:43:27 +0900</pubDate>
</item>
<item>
<title>[PHP] 연락처가 올바른 연락처인지 검증하는 함수</title>
<link>https://note.chanyeongpark.com/posts/php/5</link>
<description>연락처가 올바른 연락처인지 검증하는 함수.함수에 연락처를 인자로 전달할 때 하이픈(-)을 포함하여야 한다.- 000-000-0000 (휴대전화번호)- 000-0000-0000 (휴대전화번호)- 00-000-0000 (유선전화번호)- 00-0000-0000 (유선전화번호)- 000-000-0000 (유선전화번호)- 000-0000-0000 (유선전화번호)- 0000-0000 (대표전화번호)&amp;lt;?phpfunction get_phone_check($number){if (!preg_match(&amp;#39;/^[0-9-]/&amp;#39;, $number)) return false;if (!preg_match(&amp;#39;/^0(2|[3-9]\d{1})-?\d{3,4}-?\d{4}$|^01([016789])-?\d{3,4}-?\d{4}|\d{4}-\d{4}$/&amp;#39;, $number)) return false;return true;}?&amp;gt;</description>
<category>PHP</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/php/5</guid>
<comments>https://note.chanyeongpark.com/posts/php/5</comments>
<pubDate>Sun, 12 Mar 2023 19:36:30 +0900</pubDate>
</item>
<item>
<title>[Javascript] 연락처가 올바른 연락처인지 검증하는 함수</title>
<link>https://note.chanyeongpark.com/posts/jquery/20</link>
<description>연락처가 올바른 연락처인지 검증하는 함수.지원되는 연락처 유형은 아래와 같다.- 휴대전화 번호- 02 (서울) 번호- 000 (지역) 번호- 0000-0000 대표번호Javascriptcheck_phone = function(destPhone) {  var regex = /^[0-9-]/;  var regex2 = /^0(2|[3-9]\d{1})-?\d{3,4}-?\d{4}$|^01([016789])-?\d{3,4}-?\d{4}|\d{4}-\d{4}$/;   if (regex.test(destPhone) &amp;amp;&amp;amp; regex2.test(destPhone)) {    return true;  } else {    return false;  }};</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/20</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/20</comments>
<pubDate>Sun, 12 Mar 2023 19:34:15 +0900</pubDate>
</item>
<item>
<title>[PHP] 연락처가 휴대전화 번호인지 유선전화번호인지 확인하는 함수</title>
<link>https://note.chanyeongpark.com/posts/php/4</link>
<description>연락처가 휴대전화 번호인지 유선전화 번호인지 검증하는 함수.휴대전화 번호라면, &amp;#39;phone&amp;#39;을, 그 외 유선전화 번호라면 &amp;#39;tel&amp;#39;을 반환한다.함수에 연락처를 인자로 전달할 때 하이픈(-)을 포함하여야 한다.&amp;lt;?phpfunction get_phone_type($number)  {    $is_phone_number = preg_match(&amp;#39;/^01([016789])-?\d{3,4}-?\d{4}$/&amp;#39;, $number);    $is_tel_number = preg_match(&amp;#39;/^(0\d{1,2}[-\s]?)?\d{3,4}[-\s]?\d{4}$/&amp;#39;, $number);    if (!$is_phone_number &amp;amp;&amp;amp; !$is_tel_number) return false;       return ($is_phone_number) ? &amp;#39;phone&amp;#39; : &amp;#39;tel&amp;#39;;  }?&amp;gt;</description>
<category>PHP</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/php/4</guid>
<comments>https://note.chanyeongpark.com/posts/php/4</comments>
<pubDate>Sun, 12 Mar 2023 19:29:09 +0900</pubDate>
</item>
<item>
<title>[PHP] 숫자만 입력한 연락처에 자동으로 하이픈(-) 추가해주는 함수</title>
<link>https://note.chanyeongpark.com/posts/php/3</link>
<description>하이픈(-) 없이 입력한 휴대전화번호, 유선전화번호에 자동으로 하이픈을 반환해주는 함수.지원되는 연락처는 아래와 같다.- 000-000-0000 (휴대전화번호)- 000-0000-0000 (휴대전화번호)- 00-000-0000 (유선전화번호)- 00-0000-0000 (유선전화번호)- 000-000-0000 (유선전화번호)- 000-0000-0000 (유선전화번호)- 0000-0000 (대표전화번호)&amp;lt;?phpfunction set_add_phone_hipen($value)  {    if (!$value) return &amp;quot;&amp;quot;;    $value = preg_replace(&amp;#39;/[^0-9]/&amp;#39;, &amp;#39;&amp;#39;, $value);    $result = array();    $restNumber = &amp;quot;&amp;quot;;    if (strpos($value, &amp;quot;02&amp;quot;) === 0) {      $result[] = substr($value, 0, 2);      $restNumber = substr($value, 2);    } elseif (strpos($value, &amp;quot;1&amp;quot;) === 0) {      $restNumber = $value;    } else {      $result[] = substr($value, 0, 3);      $restNumber = substr($value, 3);    }    if (strlen($restNumber) === 7) {      $result[] = substr($restNumber, 0, 3);      $result[] = substr($restNumber, 3);         } else {      $result[] = substr($restNumber, 0, 4);      $result[] = substr($restNumber, 4);    }    return implode(&amp;quot;-&amp;quot;, array_filter($result));  }?&amp;gt;</description>
<category>PHP</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/php/3</guid>
<comments>https://note.chanyeongpark.com/posts/php/3</comments>
<pubDate>Sun, 12 Mar 2023 19:24:42 +0900</pubDate>
</item>
<item>
<title>[jQuery] 멀티 파일 업로드 플러그인 (MultiFile)</title>
<link>https://note.chanyeongpark.com/posts/jquery/19</link>
<description>:file input을 멀티파일 업로더로 손쉽게 적용해주는 jQuery 플러그인.첨부 가능 확장자, 용량 제한 등 다양한 옵션을 제공하는 간편한 플러그인이다.직관적인 작동 방식으로 커스터마이징이 편하다.HTML&amp;lt;input type=&amp;quot;file&amp;quot; name=&amp;quot;attach&amp;quot; id=&amp;quot;attach&amp;quot;&amp;gt;&amp;lt;div class=&amp;quot;attaedbox&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;jQuery&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;$(&amp;#39;:file[name=attach]&amp;#39;).MultiFile({  &amp;#39;max&amp;#39; : 5,  &amp;#39;accept&amp;#39; : &amp;#39;gif|jpg|jpeg|bmp|png|pdf&amp;#39;,  &amp;#39;maxfile&amp;#39; : 10240, //각 파일 최대 업로드 크기 (kb)  &amp;#39;list&amp;#39; : $(&amp;#39;:file[name=attachedbox]&amp;#39;), //파일목록을 출력할 요소 지정가능  &amp;#39;STRING&amp;#39; : { // error문구를 원하는대로 수정    &amp;#39;remove&amp;#39; : &amp;quot;&amp;lt;a href=\&amp;quot;#\&amp;quot; class=\&amp;quot;remove\&amp;quot;&amp;gt;제거&amp;lt;/a&amp;gt;&amp;quot;,    &amp;#39;duplicate&amp;#39; : &amp;quot;$file 은 이미 선택된 파일입니다.&amp;quot;,    &amp;#39;denied&amp;#39; : &amp;quot;$ext 는(은) 업로드 할수 없는 파일확장자입니다.&amp;quot;,    &amp;#39;selected&amp;#39; :&amp;#39;$file 을 선택했습니다.&amp;#39;,    &amp;#39;toomuch&amp;#39; : &amp;quot;업로드할 수 있는 최대크기를 초과하였습니다.($size)&amp;quot;,    &amp;#39;toomany&amp;#39; : &amp;quot;업로드할 수 있는 최대 개수는 $max개 입니다.&amp;quot;,    &amp;#39;toobig&amp;#39; : &amp;quot;$file 은 크기가 매우 큽니다. (max $size)&amp;quot;  }});&amp;lt;/script&amp;gt;</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/19</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/19</comments>
<pubDate>Sun, 12 Mar 2023 12:25:36 +0900</pubDate>
</item>
<item>
<title>[Javascript] text input에 연락처 입력시 자동 하이픈(-) 처리</title>
<link>https://note.chanyeongpark.com/posts/jquery/18</link>
<description>text input에 연락처를 입력하는 경우 자동으로 하이픈(-) 처리하는 코드.지원되는 연락처 유형은 아래와 같다.- 휴대전화 번호- 02 (서울) 번호- 000 (지역) 번호- 0000-0000 대표번호HTML&amp;lt;input type=&amp;quot;text&amp;quot; callnumber /&amp;gt;Javascript$(document).on(&amp;#39;keyup&amp;#39;, &amp;#39;input[callnumber]&amp;#39;, function() {  var value = $(this).val();  if (!value) return &amp;quot;&amp;quot;;  value = value.replace(/[^0-9]/g, &amp;quot;&amp;quot;);  let result = [];  let restNumber = &amp;quot;&amp;quot;;  // 지역번호와 나머지 번호로 나누기  if (value.startsWith(&amp;quot;02&amp;quot;)) {  // 서울 02 지역번호  result.push(value.substr(0, 2));  restNumber = value.substring(2);  } else if (value.startsWith(&amp;quot;1&amp;quot;)) {  // 지역 번호가 없는 경우  // 1xxx-yyyy  restNumber = value;  } else {  // 나머지 3자리 지역번호  // 0xx-yyyy-zzzz  result.push(value.substr(0, 3));  restNumber = value.substring(3);  }  if (restNumber.length === 7) {  // 7자리만 남았을 때는 xxx-yyyy  result.push(restNumber.substring(0, 3));  result.push(restNumber.substring(3));  } else {  result.push(restNumber.substring(0, 4));  result.push(restNumber.substring(4));  }  $(this).val(result.filter((val) =&amp;gt; val).join(&amp;quot;-&amp;quot;));});</description>
<category>Javascript</category>
<author>DevNote</author>
<guid isPermaLink="true">https://note.chanyeongpark.com/posts/jquery/18</guid>
<comments>https://note.chanyeongpark.com/posts/jquery/18</comments>
<pubDate>Fri, 10 Mar 2023 14:27:56 +0900</pubDate>
</item>
</channel>
</rss>