<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Mr.Smith is busy.</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 13 Oct 2022 05:47:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Mr.Smith is busy.</title>
            <url>https://images.velog.io/images/smith_94/profile/e59ac6f4-d11d-4765-8663-fa52f5f63871/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Mr.Smith is busy.. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/smith_94" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[NodeJS] Socket.io]]></title>
            <link>https://velog.io/@smith_94/NodeJS-Socket.io</link>
            <guid>https://velog.io/@smith_94/NodeJS-Socket.io</guid>
            <pubDate>Thu, 13 Oct 2022 05:47:40 GMT</pubDate>
            <description><![CDATA[<h3 id="serverjs">server.js</h3>
<pre><code>const sessionMiddleware = session({
    resave: false,
    saveUninitialized: false,
    secret: process.env.COOKIE_SECRET,
    cookie: {
        httpOnly: true,
        secure: false
    }
});
app.use(sessionMiddleware);</code></pre><h3 id="iojs">io.js</h3>
<pre><code>const {Server} = require(&#39;socket.io&#39;);

module.exports = (server, app, sessionMiddleware) =&gt; {
    const io = new Server(server, {
        path: &#39;/socket.io&#39;,
        cors: {
            origin: &#39;*&#39;,
            credentials: true
        }
    });
    const namespace = io.of(&#39;/namespace&#39;);
    app.set(&#39;io&#39;, io);

    io.use((socket, next) =&gt; {
        sessionMiddleware(socket.request, socket.request.res || {}, next);
    });

    namespace.on(&#39;connect&#39;, (socket) =&gt; {
        console.log(&#39;user connected&#39;);

        socket.join(room);

        socket.on(&#39;event-name&#39;, (data) =&gt; {
            namespace.to(room).emit(&#39;event-name&#39;, { data });
        });

        socket.on(&#39;disconnect&#39;, () =&gt; console.log(&#39;user disconnected&#39;));
    });

    return io;
}</code></pre><h3 id="routerjs">router.js</h3>
<pre><code>const express = require(&#39;express&#39;);
const router = express.Router();

router.get(&#39;/&#39;, (req, res, next) =&gt; {
        req.app.get(&#39;io&#39;).of(&#39;/namespace&#39;).to(room).emit(&#39;event-name&#39;, {});
});

module.exports = router;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[글자 받침만 확인]]></title>
            <link>https://velog.io/@smith_94/%EA%B8%80%EC%9E%90-%EB%B0%9B%EC%B9%A8%EB%A7%8C-%ED%99%95%EC%9D%B8</link>
            <guid>https://velog.io/@smith_94/%EA%B8%80%EC%9E%90-%EB%B0%9B%EC%B9%A8%EB%A7%8C-%ED%99%95%EC%9D%B8</guid>
            <pubDate>Wed, 10 Aug 2022 01:41:10 GMT</pubDate>
            <description><![CDATA[<h3 id="글자-받침만-확인">글자 받침만 확인</h3>
<pre><code>function checkFinalConsonant(txt: string) {
    // 입력받은 문자를 유니코드로 변경
    const uni = txt.charCodeAt(txt.length-1) - 44032;

    // 입력받은 문자가가 없을 때 빈 문자열 반환
    if (txt.length == 0) {
        return &#39;&#39;;
    }

    // 한글이 아닐때 입력받은 문자를 반환
    if (uni &lt; 0 || uni &gt; 11171) {
        return txt;
    }

    // 받침이 없을 경우 false, 있을 경우 true를
    return (uni % 28 === 0);
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[글자 찾기]]></title>
            <link>https://velog.io/@smith_94/%EA%B8%80%EC%9E%90-%EC%B0%BE%EA%B8%B0</link>
            <guid>https://velog.io/@smith_94/%EA%B8%80%EC%9E%90-%EC%B0%BE%EA%B8%B0</guid>
            <pubDate>Tue, 09 Aug 2022 09:05:56 GMT</pubDate>
            <description><![CDATA[<h3 id="글자-찾기">글자 찾기</h3>
<pre><code>function searchWord(target: string, str: number | string) {
    let searchArr: string[] = [];
    const newStr = str.toString();
    let index = 0;
    let result: string;
    let sliceIndex: number;

    if (target.length &lt; newStr.length) {
        return &quot;Failed&quot;;
    }

    for (let letter of target) {
        const regex = new RegExp(/[^0-9a-zA-Z가-힣]/g);
        if (letter === newStr[index]) {
            searchArr.push(letter);
            index++;
        } else if (searchArr.length !== 0 &amp;&amp; regex.test(letter)) {
            searchArr.push(letter);
        } else {
            index = 0;
            searchArr = [];
        }

        if (index === newStr.length) {
            break;
        }
    }

    result = searchArr.join(&#39;&#39;);
    sliceIndex = target.indexOf(result);

    return (
         target.slice(0, sliceIndex) +
        `&lt;span&gt;` +
        target.slice(sliceIndex, sliceIndex + result.length) +
        `&lt;/span&gt;` +
        target.slice(sliceIndex + result.length, target.length)
    );
}

const list = [
    &quot;TEST 테%스%트 아이^스아메리#카)노 3,500원@@@&quot;,
    &quot;TEST 테%스%트 아@이^스아메리#카노 3,500원@@@&quot;,
    &quot;TEST 테%스%트 아이^스아메$리#카(노 3,500원@@@&quot;,
    &quot;TEST 테%스%트 아이^스아^메리#카노 3,500원@@@&quot;,
    &quot;TEST 테%스%트 아이^스#아메#리카*노 3,500원@@@&quot;
];
const searchResult = [];

for (let item of list) {
    searchResult.push(searchWord(item, 아이스아메리카노));
}

console.log(searchResult);</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[자음 모음 분리하기]]></title>
            <link>https://velog.io/@smith_94/Javascript-%EC%9E%90%EC%9D%8C-%EB%AA%A8%EC%9D%8C-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@smith_94/Javascript-%EC%9E%90%EC%9D%8C-%EB%AA%A8%EC%9D%8C-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 09 Aug 2022 08:59:15 GMT</pubDate>
            <description><![CDATA[<h3 id="자음-모음-분리하기">자음 모음 분리하기</h3>
<pre><code>function getConstantVowel(letter: string) {
    const consonantArr = [&#39;ㄱ&#39;, &#39;ㄲ&#39;, &#39;ㄴ&#39;, &#39;ㄷ&#39;, &#39;ㄸ&#39;, &#39;ㄹ&#39;, &#39;ㅁ&#39;,
               &#39;ㅂ&#39;, &#39;ㅃ&#39;, &#39;ㅅ&#39;, &#39;ㅆ&#39;, &#39;ㅇ&#39;, &#39;ㅈ&#39;, &#39;ㅉ&#39;,
               &#39;ㅊ&#39;, &#39;ㅋ&#39;, &#39;ㅌ&#39;, &#39;ㅍ&#39;, &#39;ㅎ&#39;];

    const vowelArr = [&#39;ㅏ&#39;, &#39;ㅐ&#39;, &#39;ㅑ&#39;, &#39;ㅒ&#39;, &#39;ㅓ&#39;, &#39;ㅔ&#39;, &#39;ㅕ&#39;,
            &#39;ㅖ&#39;, &#39;ㅗ&#39;, &#39;ㅘ&#39;, &#39;ㅙ&#39;, &#39;ㅚ&#39;, &#39;ㅛ&#39;, &#39;ㅜ&#39;,
            &#39;ㅝ&#39;, &#39;ㅞ&#39;, &#39;ㅟ&#39;, &#39;ㅠ&#39;, &#39;ㅡ&#39;, &#39;ㅢ&#39;, &#39;ㅣ&#39;];

    const finalConsonantArr = [&#39;&#39;, &#39;ㄱ&#39;, &#39;ㄲ&#39;, &#39;ㄳ&#39;, &#39;ㄴ&#39;, &#39;ㄵ&#39;, &#39;ㄶ&#39;,
            &#39;ㄷ&#39;, &#39;ㄹ&#39;, &#39;ㄺ&#39;, &#39;ㄻ&#39;, &#39;ㄼ&#39;, &#39;ㄽ&#39;, &#39;ㄾ&#39;,
            &#39;ㄿ&#39;, &#39;ㅀ&#39;, &#39;ㅁ&#39;, &#39;ㅂ&#39;, &#39;ㅄ&#39;, &#39;ㅅ&#39;, &#39;ㅆ&#39;,
            &#39;ㅇ&#39;, &#39;ㅈ&#39;, &#39;ㅊ&#39;, &#39;ㅋ&#39;, &#39;ㅌ&#39;, &#39;ㅍ&#39;, &#39;ㅎ&#39;];

    const firstLetter = 44032;
    let uni = letter.charCodeAt(0);

    uni = uni - firstLetter;

    let consonantUni = parseInt((uni / 588).toString());
    let vowelUni = parseInt(((uni - (consonantUni * 588)) / 28).toString());
    let finalConsonantUni = parseInt((uni % 28).toString());

    return {
        consonant: consonantArr[consonantUni],
        vowel: vowelArr[vowelUni],
        finalConsonant: finalConsonantArr[finalConsonantUni]
    };
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Angular 언어 변경 감지]]></title>
            <link>https://velog.io/@smith_94/Angular-%EC%96%B8%EC%96%B4-%EB%B3%80%EA%B2%BD-%EA%B0%90%EC%A7%80</link>
            <guid>https://velog.io/@smith_94/Angular-%EC%96%B8%EC%96%B4-%EB%B3%80%EA%B2%BD-%EA%B0%90%EC%A7%80</guid>
            <pubDate>Fri, 22 Jul 2022 03:09:10 GMT</pubDate>
            <description><![CDATA[<p>Angular 언어 변경 감지</p>
<pre><code>    constructor(
      private translate: TranslateService
    ) {}

    this.translate.onLangChange.subscribe((event: LangChangeEvent) =&gt; {
      console.log(event.lang);
    });</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Angular form]]></title>
            <link>https://velog.io/@smith_94/Angular-form</link>
            <guid>https://velog.io/@smith_94/Angular-form</guid>
            <pubDate>Wed, 29 Jun 2022 07:00:05 GMT</pubDate>
            <description><![CDATA[<p>ts 파일에 FormBuilder, FormGroup, FormControl을 직접 선언하여 사용하지 않고 간단하게 전송 테스트를 진행할 수 있다.</p>
<pre><code>// html
&lt;div&gt;
  &lt;div&gt;
    &lt;form #authForm=&quot;ngForm&quot; (ngSubmit)=&quot;onSubmit(authForm)&quot;&gt;
      &lt;div&gt;
        &lt;label for=&quot;email&quot;&gt;E-mail&lt;/label&gt;
        &lt;input type=&quot;email&quot; id=&quot;email&quot; ngModel name=&quot;email&quot; required email&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;label for=&quot;password&quot;&gt;&lt;/label&gt;
        &lt;input type=&quot;password&quot; id=&quot;password&quot; ngModel name=&quot;password&quot; required minlength=&quot;6&quot;&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;button type=&quot;submit&quot; [disabled]=&quot;!authForm.valid&quot;&gt;Submit&lt;/button&gt;
      &lt;/div&gt;
    &lt;/form&gt;
  &lt;/div&gt;
&lt;/div&gt;


// ts
onSubmit(form: NgForm): void {
  console.log(form.value);    // {email: &#39;&#39;, password: &#39;&#39;}
  form.reset();
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Angular resolve]]></title>
            <link>https://velog.io/@smith_94/Angular-resolve</link>
            <guid>https://velog.io/@smith_94/Angular-resolve</guid>
            <pubDate>Wed, 29 Jun 2022 06:30:38 GMT</pubDate>
            <description><![CDATA[<p>resolve 서비스를 이용하면 라우팅할 때 데이터를 미리 불러올 수 있다.</p>
<ol>
<li>resolve 서비스 생성<pre><code>// resolve.service.ts
</code></pre></li>
</ol>
<p>@Injectable({
  providedIn: &#39;root&#39;
})</p>
<p>export class ResolveService implements Resolve<any> {
  constructor(
    private service: AppService
  ) {
  }</p>
<p>  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): any {
    return this.service.getData().pipe(
      mergeMap((res: HttpResponse<any>) =&gt; {
        if (res.body) {
          return of(res.body);
        } else {
          return EMPTY;
        }
      })
    );
  }
}</p>
<pre><code>
2. 라우터 모듈에 추가</code></pre><p>// app.module.ts</p>
<p>@NgModule({
  ...
  imports: [
    ...
    RouterModule.forRoot([
      {
        path: &#39;&#39;,
        component: ChildComponent,
        resolve: {
          resolveData: ResolveService
        }
      }
    ])
  ],
  ...
})</p>
<pre><code>
3. resolve로 미리 로드한 데이터 사용</code></pre><p>// component.ts</p>
<p>this.activatedRoute.data.subscribe(res =&gt; {
  console.log(res);
});
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP Interceptor]]></title>
            <link>https://velog.io/@smith_94/HTTP-Interceptor</link>
            <guid>https://velog.io/@smith_94/HTTP-Interceptor</guid>
            <pubDate>Wed, 29 Jun 2022 03:04:35 GMT</pubDate>
            <description><![CDATA[<h3 id="angular-httpinterceptor-적용-예시">Angular HttpInterceptor 적용 예시</h3>
<ol>
<li>http interceptor 서비스 생성</li>
</ol>
<pre><code>// auth-interceptor.service.ts

export class AuthInterceptorService implements HttpInterceptor {
  intercept(req: HttpRequest&lt;any&gt;, next: HttpHandler): Observable&lt;HttpEvent&lt;any&gt;&gt; {
    ...
    // 인증 확인 후 http 헤더 수정
    ...
   return next.handle(req).pipe(tap(event =&gt; {
      if (event instanceof HttpResponse) {
        ...
        // 응답이 포함된 요청일 경우 인증 갱신
        ...
      }
    }));
  }
}</code></pre><ol start="2">
<li>모듈에 추가<pre><code>// app.module.ts
</code></pre></li>
</ol>
<p>@NgModule({
  ...
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptorService,
      multi: true    // 여러개의 인터셉터 사용 여부
    }
  ],
})
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Javascript URL 파라미터 참조하기]]></title>
            <link>https://velog.io/@smith_94/Javascript-URL-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EC%B0%B8%EC%A1%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@smith_94/Javascript-URL-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EC%B0%B8%EC%A1%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 28 Jun 2022 07:53:47 GMT</pubDate>
            <description><![CDATA[<p>url에서 파라미터를 가져와 사용할 때 쓰면 편하다.</p>
<p>사용 예시만 남겨놓는다.</p>
<p>const params = new URLSearchParams(location.search);
const paramsA = params.get(&#39;A&#39;);</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Angular 라우팅 요청 차단하는 방법]]></title>
            <link>https://velog.io/@smith_94/Angular-%EB%9D%BC%EC%9A%B0%ED%8C%85-%EC%9A%94%EC%B2%AD-%EC%B0%A8%EB%8B%A8%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@smith_94/Angular-%EB%9D%BC%EC%9A%B0%ED%8C%85-%EC%9A%94%EC%B2%AD-%EC%B0%A8%EB%8B%A8%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Thu, 23 Jun 2022 05:33:51 GMT</pubDate>
            <description><![CDATA[<h3 id="오늘은">오늘은</h3>
<p>Angular에서 제공하는 CanDeactivate를 사용해 보았다.</p>
<p>CanDeactivate는 현재 컴포넌트에서 다른 컴포넌트로 라우팅을 요청할 때, 임의의 조건에 따라 라우팅을 승인하거나 차단할 수 있게 해주는 기능이다.</p>
<h3 id="1-can-deactivate-guardservicets-생성">1. can-deactivate-guard.service.ts 생성</h3>
<p>can-deactivate-guard.service.ts 파일을 생성한다.</p>
<pre><code>import {Observable} from &quot;rxjs&quot;;
import {ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot} from &quot;@angular/router&quot;;

export interface CanComponentDeactivate {
  canDeactivate: () =&gt; Observable&lt;boolean&gt; | Promise&lt;boolean&gt; | boolean;
}

export class CanDeactivateGuard implements CanDeactivate&lt;CanComponentDeactivate&gt; {
  canDeactivate(
    component: CanComponentDeactivate,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState?: RouterStateSnapshot
  ) : Observable&lt;boolean&gt; | Promise&lt;boolean&gt; | boolean {
    return component.canDeactivate();
  }
}
</code></pre><h3 id="2-module-수정">2. module 수정</h3>
<p>module.ts의 providers에 canDeactivate를 추가한다.</p>
<pre><code>  providers: [
    ...
    CanDeactivateGuard
  ]</code></pre><h3 id="3-route-수정">3. route 수정</h3>
<pre><code>    {
      path: &#39;&#39;,
      ...
      canDeactivate: [CanDeactivateGuard]
    },</code></pre><h3 id="4-componentts에-candeactivate-함수-생성">4. component.ts에 canDeactivate 함수 생성</h3>
<p>canDeactivate에서 리턴되는 결과에 따라 라우팅 요청이 성공하거나 취소된다.</p>
<pre><code>  canDeactivate(): Observable&lt;boolean&gt; | Promise&lt;boolean&gt; | boolean {
    if (this.infoChange) {
      return confirm(&#39;Message&#39;);
    } else {
      return true;
    }
  }</code></pre><h3 id="5-custom-dialog-사용">5. custom dialog 사용</h3>
<p>아래 코드에서는 Material dialog를 사용했다.
dialog close 이벤트에 boolean 값을 리턴한다.</p>
<pre><code>  canDeactivate(): Observable&lt;boolean&gt; | Promise&lt;boolean&gt; | boolean {
    if (this.infoChange) {
      const dialogRef = this.dialog.open(ConfirmRouteComponent);
      return dialogRef.afterClosed();
    } else {
      return true;
    }
  }</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Angular 프로젝트 Electron 앱으로 배포하기]]></title>
            <link>https://velog.io/@smith_94/Angular-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Electron-%EC%95%B1%EC%9C%BC%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@smith_94/Angular-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Electron-%EC%95%B1%EC%9C%BC%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 17 Jun 2022 03:30:00 GMT</pubDate>
            <description><![CDATA[<h3 id="0-angular-프로젝트-생성">0. Angular 프로젝트 생성</h3>
<p>프로젝트 생성 후, index.html 파일의 base href를 &quot;./&quot;로 수정한다.</p>
<h3 id="1-electron-설치">1. Electron 설치</h3>
<pre><code>npm install electron --save-dev</code></pre><h3 id="2-mainjs-파일-생성">2. main.js 파일 생성</h3>
<p><img src="https://velog.velcdn.com/images/smith_94/post/5a97d95d-0f29-471b-8453-4fafe3f69be1/image.png" alt=""></p>
<pre><code>const {app, BrowserWindow} = require(&#39;electron&#39;);

let win;

function createWindow() {
  win = new BrowserWindow({
    width: 600,
    height: 600,
    backgroundColor: &#39;#fff&#39;
  })

  win.loadURL(`file://${__dirname}/dist/electron-app/index.html`)

  win.webContents.openDevTools()

  win.on(&#39;closed&#39;, function () {
    win = null
  })
}

app.on(&#39;ready&#39;, createWindow)

app.on(&#39;window-all-closed&#39;, function () {
  if (process.platform !== &#39;darwin&#39;) app.quit()
})

app.on(&#39;activate&#39;, function () {
  if (win === null) createWindow()
})
</code></pre><p>Electron에서 제공하는 BrowserWindow API를 사용하여 GUI 윈도우를 만들고, loadURL() 메서드로 dist 폴더의 index.html 파일을 불러온다. dist 폴더는 프로젝트를 빌드하면 생성된다.</p>
<p>앱을 실행할 때 index.html의 경로를 확실하게 작성해야 화면이 정상적으로 출력된다.</p>
<h3 id="3-packagejson-파일-수정">3. package.json 파일 수정</h3>
<p>package.json 파일에서 메인 엔트리 포인트를 수정한다.
쉽게 프로젝트를 빌드하고 실행할 수 있도록 스크립트를 추가한다.</p>
<pre><code>{
  &quot;name&quot;: &quot;electron-app&quot;,
  &quot;version&quot;: &quot;0.0.0&quot;,
  &quot;main&quot;: &quot;electron-main.js&quot;,
  &quot;scripts&quot;: {
      ...
    &quot;start:electron&quot;: &quot;ng build --prod &amp;&amp; electron .&quot;
  },
}</code></pre><h3 id="4-electron-packager-설치-및-스크립트-추가">4. electron-packager 설치 및 스크립트 추가</h3>
<p>데스크탑 앱을 배포하기 위해 electron-packager를 설치하고, package.json에 스크립트를 추가한다.</p>
<pre><code>npm install electron-packager --save-dev

// package.json
&quot;pack:electron-win&quot;: &quot;electron-packager . --platform=win32&quot;,    // Window
&quot;pack:electron-darwin&quot;: &quot;electron-packager . --platform=darwin&quot;    // Mac</code></pre><h3 id="5-windows-앱-패키징">5. Windows 앱 패키징</h3>
<p>위에서 작성한 스크립트를 입력하면 .exe 파일이 생성된다.
윈도우에서 해당 파일을 실행하면 Electron 앱에서 Angular 프로젝트가 실행된다.
<img src="https://velog.velcdn.com/images/smith_94/post/eeb45164-004c-4f42-a6ee-5c04443389c2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[그냥 살기 - 이미지 압축 다운로드]]></title>
            <link>https://velog.io/@smith_94/%EA%B7%B8%EB%83%A5-%EC%82%B4%EA%B8%B0-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%95%95%EC%B6%95-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C</link>
            <guid>https://velog.io/@smith_94/%EA%B7%B8%EB%83%A5-%EC%82%B4%EA%B8%B0-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%95%95%EC%B6%95-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C</guid>
            <pubDate>Tue, 14 Jun 2022 06:38:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>오늘이 어쩌고 내일이 저쩌고 포기했다.
그냥 살기로 결정했다.</p>
</blockquote>
<h3 id="오늘은">오늘은</h3>
<p>그래프와 이미지를 압축 파일로 다운로드하는 방법이다.
다운로드할 파일은 Apache ECharts와 PC에 있는 이미지를 사용했다.
압축에는 JSZip를 사용했다.
내가 보려고 메모하는 게 목적이라 코드에 대한 자세한 설명은 생략한다.</p>
<h3 id="testcomponenthtml">test.component.html</h3>
<ul>
<li>그래프 이미지 다운로드<pre><code>&lt;button (click)=&quot;imageDownload(&#39;canvas&#39;)&quot;&gt;Download&lt;/button&gt;
</code></pre></li>
</ul>
<article #canvas>
  <section *ngFor="let chart of [1, 2, 3, 4, 5]">
    <div echarts [options]="chartOption"></div>
  </section>
</article>
```
> 그래프를 컴포넌트로 분리하고 *ngFor 디렉티브와 @Input 데코레이터를 사용하면
> 하나의 그래프 컴포넌트로 여러 개의 데이터를 출력할 수 있다.

<ul>
<li>일반 이미지 다운로드<pre><code>&lt;button (click)=&quot;imageDownload(&#39;img&#39;)&quot;&gt;Download&lt;/button&gt;
</code></pre></li>
</ul>
<article #image>
  <section>
    <img src="../content/image/tiger1.jpg" alt="tiger1">
    <img src="../content/image/tiger2.jpg" alt="tiger2">
  </section>
</article>
```
<br>

<h3 id="testcomponentts">test.component.ts</h3>
<pre><code>  chartOption: EChartsOption = {};
  container = new Map;

  @ViewChild(&#39;canvas&#39;) canvas! : ElementRef;
  @ViewChild(&#39;image&#39;) image!: ElementRef;

  constructor() {}

  ngOnInit(): void {
    this.createChart();
  }

  ngAfterViewInit(): void {
    this.container.set(&#39;canvas&#39;, this.canvas);
    this.container.set(&#39;img&#39;, this.image);
  }

  createChart(): void {...}

  imageDownload(type: string): void {
    const zip = new JSZip();
    const targetImages = this.container.get(type).nativeElement.getElementsByTagName(type);

    for (let i = 0; i &lt; targetImages.length; i++) {
      let canvas = targetImages[i];
      let fileName = `image_${i+1}.jpg`;
      if (type === &#39;img&#39;) {
        fileName = `${canvas.alt}.jpg`;
        canvas = document.createElement(&#39;canvas&#39;);
        canvas.width = targetImages[i].width;
        canvas.height = targetImages[i].height;
        const ctx = canvas.getContext(&#39;2d&#39;);
        ctx.drawImage(targetImages[i], 0, 0);
      }
      const url = canvas.toDataURL(&#39;image/*&#39;);
      const base64 = url.replace(/^data:image\/[a-z]+;base64,/, &quot;&quot;);
      zip.file(fileName, base64, {&#39;base64&#39;: true});
    }

    zip.generateAsync({type: &#39;base64&#39;}).then(
      file =&gt; {
        const link = document.createElement(&#39;a&#39;);
        link.setAttribute(&#39;href&#39;, &#39;data:application/octastream;base64, &#39;+ file);
        link.setAttribute(&#39;download&#39;, &#39;example.zip&#39;);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
      }
    )
  }</code></pre><blockquote>
<p>두 번 쓰기 번거로워 한 번에 정리했다.
혹시나 개발에 참고한다면 잘 구분하고 수정해서 사용할 것을 권장한다.</p>
</blockquote>
<hr>
<pre><code>    // canvas background color setting, 설정하지 않으면 배경이 투명하게 나오는 경우가 있음
    const ctx = canvas.getContext(&#39;2d&#39;);
    ctx.globalCompositeOperation = &#39;color-burn&#39; || &#39;destination-atop&#39; || &#39;destination-over&#39;;
    ctx.fillStyle = &quot;white&quot;;
    ctx.fillRect(0, 0, canvas.width, canvas.height);</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[적당히 살기 2일차 - Angular Pipe]]></title>
            <link>https://velog.io/@smith_94/%EC%A0%81%EB%8B%B9%ED%9E%88-%EC%82%B4%EA%B8%B0-2%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@smith_94/%EC%A0%81%EB%8B%B9%ED%9E%88-%EC%82%B4%EA%B8%B0-2%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Mon, 13 Jun 2022 02:23:33 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>나름대로 적당히 살기 2일차
내일이 3일차가 아닐 수도 있음</p>
</blockquote>
<h3 id="오늘은">오늘은</h3>
<p>앵귤러에서 파이프를 적용하는 방법이다.</p>
<p>앵귤러에 적응하고 있을 때 데이터를 변환하여 출력하기 위해 파이프를 사용해야 했던 적이 있었다. 가이드 문서에 충분히 설명되어 있지만, html 컴포넌트와 ts 컴포넌트를 분리해서 사용할 때 ts 컴포넌트에 파이프를 직접 적용하는 방법에 대해서는 설명이 충분하지 않았던 것으로 기억된다. 그냥 못 본 것일 수도 있다. 언젠가 또 쓸 일이 있지 않을까 싶어 정리한다.</p>
<h3 id="1-파이프를-생성한다">1. 파이프를 생성한다.</h3>
<p>파이프 만드는건 공식 문서에 친절하게 설명되어 있다.</p>
<h3 id="2-서비스와-같은-방법으로-사용한다">2. 서비스와 같은 방법으로 사용한다.</h3>
<p>아래 코드처럼 component.ts에 서비스를 주입하듯이 사용하면 된다.
한 가지 차이점이 있다면 컴포넌트 데코레이터에 providers를 추가해야 한다.
모듈에 한 번만 추가해서 썼던 것도 같은데 기억이 잘 안 난다. 나중에 확인해 봐야겠다.</p>
<blockquote>
<pre><code>@Component({
   ...
    providers: [Pipe]
})

constructor(
   private pipe: Pipe
) {}

v = this.pipe.transform(value, ...args);</code></pre></blockquote>
<p>```</p>
<p>-끝-</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[적당히 살기 1일차 - 리팩토링]]></title>
            <link>https://velog.io/@smith_94/%EC%A0%81%EB%8B%B9%ED%9E%88-%EC%82%B4%EA%B8%B0-1%EC%9D%BC%EC%B0%A8-h75o0tll</link>
            <guid>https://velog.io/@smith_94/%EC%A0%81%EB%8B%B9%ED%9E%88-%EC%82%B4%EA%B8%B0-1%EC%9D%BC%EC%B0%A8-h75o0tll</guid>
            <pubDate>Thu, 09 Jun 2022 09:30:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>나름대로 적당히 살기 1일차
내일이 2일차가 아닐 수도 있음</p>
</blockquote>
<h3 id="오늘은">오늘은</h3>
<p>리팩토링이다. 아래 책을 참고했다.
책이 조금 어려울 수 있다. 그래서 쉬운 것만 정리했다.
나머지는 나 혼자 봤다. 궁금하면 찾아보는 것을 추천한다.</p>
<p><a href="http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&amp;mallGb=KOR&amp;barcode=9791162242742&amp;orderClick=LAG&amp;Kc=#N"><img src="https://velog.velcdn.com/images/smith_94/post/d5cdc962-3bda-4268-8601-754dde4772a1/image.jpg" width="40%"></a></p>
<h2 id="정의">정의</h2>
<p>&amp;nbsp여러 가지 리팩토링 기법을 적용하여 코드를 이해하고 수정하기 쉽도록 내부 구조를 재구성한다. 이때, 리팩토링 전과 후의 프로그램 동작은 동일해야 한다.</p>
<p>&amp;nbsp리팩토링 후, 프로그램의 성능이 반드시 좋아지는 것은 아니다. 오히려 나빠질 수 있으며, 성능이 좋아지는 것을 목표로 한다면 코드는 더 어렵게 바뀔 수 있다.</p>
<hr>
<h2 id="이유">이유</h2>
<p>&amp;nbsp목표 달성만을 위한 코딩이 반복된다면 여러 곳에서 중복된 코드가 발생하거나 프로그램의 내부 구조가 복잡해질 수 있다. 이는 프로그램의 설계를 파악하거나 코드를 이해하는 데에 어려움을 줄 수 있다. 결과적으로 기존의 기능을 수정하거나 새로운 기능을 추가하는 데에 더 많은 시간이 소요되게 한다.</p>
<hr>
<h2 id="목적">목적</h2>
<ol>
<li>내부 구조를 명확하게 한다.</li>
<li>코드의 의도를 명확하게 하고, 이해하기 쉽게 한다.</li>
</ol>
<hr>
<h2 id="목표">목표</h2>
<p>&amp;nbsp코드의 구조를 쉽게 파악할 수 있도록 하여, 새로운 기능을 추가하거나 기존의 기능을 수정하는 데에 소요되는 시간을 단축시킨다.</p>
<hr>
<h2 id="방법">방법</h2>
<h4 id="1-함수와-변수의-이름을-변경한다">1. 함수와 변수의 이름을 변경한다.</h4>
<ul>
<li>이름만 보고도 어떻게 사용되는지 명확하게 알 수 있어야 한다.</li>
</ul>
<h4 id="2-중복된-코드를-정리한다">2. 중복된 코드를 정리한다.</h4>
<ul>
<li>중복된 코드를 하나로 통합하여 사용한다. 똑같은 구조나 기능을 가진 코드가 여러 곳에서 존재하고 있을 때, 해당 기능을 수정하면 비슷한 코드들을 전부 확인해야 하는 소요가 있을 수 있다.</li>
</ul>
<h4 id="3-함수를-분리한다">3. 함수를 분리한다.</h4>
<ul>
<li>함수를 기능적으로 구분하여 분리하고, 간접 호출하여 사용한다. 함수 내에서 주석으로 구분해야 하는 영역이나 조건문, 반복문도 대상이 될 수 있다. 이때, 함수의 이름은 코드의 목적이 드러나도록 설정한다.</li>
<li>지나친 분리로 쓸데없는 간접 호출이 발생하는 경우를 주의한다.</li>
</ul>
<h4 id="4-복잡한-표현식을-변수-또는-함수로-추출한다">4. 복잡한 표현식을 변수 또는 함수로 추출한다.</h4>
<ul>
<li>표현식이 복잡하여 이해하기 어려운 경우, 변수나 별도의 함수로 추출하여 사용할 수 있다. 이때, 변수나 함수의 이름은 코드의 의도가 명확하게 드러나도록 설정한다.</li>
</ul>
<h4 id="5-반복문을-파이프-라인으로-변경한다">5. 반복문을 파이프 라인으로 변경한다.</h4>
<ul>
<li>반복문과 조건문으로 구성된 코드를 파이프 라인으로 수정하면 처리 과정을 쉽게 이해할 수 있다.</li>
</ul>
<h4 id="6-사용하지-않는-코드를-제거한다">6. 사용하지 않는 코드를 제거한다.</h4>
<ul>
<li>사용하지 않는 변수나 호출되지 않는 함수를 제거한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[열심히 살기 1일차]]></title>
            <link>https://velog.io/@smith_94/%EC%97%B4%EC%8B%AC%ED%9E%88-%EC%82%B4%EA%B8%B0-1%EC%9D%BC%EC%B0%A8-nvy51jqz</link>
            <guid>https://velog.io/@smith_94/%EC%97%B4%EC%8B%AC%ED%9E%88-%EC%82%B4%EA%B8%B0-1%EC%9D%BC%EC%B0%A8-nvy51jqz</guid>
            <pubDate>Fri, 03 Jun 2022 05:32:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>나름대로 열심히 살기 1일차
내일이 2일차가 아닐 수도 있음</p>
</blockquote>
<h3 id="개발-인생-3줄-요약">개발 인생 3줄 요약</h3>
<ol>
<li>자바스크립트와 리액트를 맛만 보고 어찌저찌 취업에 성공했다.</li>
<li>입사 후 앵귤러를 배워 만 1년하고도 3일째 프론트앤드 개발자로 일하고 있다.</li>
<li>그나마 조금 알던 리액트를 다 까먹었고, 리액트가 차지하고 있던 뇌를 뷰로 다시 채우고 있다.</li>
</ol>
<h3 id="블로그를-시작하려는-거창한-이유">블로그를 시작하려는 거창한 이유</h3>
<p>취업 후 대학원에 복학했고, 가끔은 알고리즘 공부도 한다. 뭐 재밋는거 없나 여기저기 기웃거리기도 하고, 어제부터는 뷰 공부도 시작했다. 뭔가 이것저것 하고 있는 것 같기는 한데 열심히 하고 있는지는 모르겠다. 사실 그냥 흘러가는 대로 살았다. 그래서 블로그에 글이라도 남겨 스스로 위안을 삼고자 한다. 무언가 했다고 증거를 남겨놓고 열심히 살았다 생각하기로 했다. 인생 결국 자기만족 아니던가.</p>
<h3 id="내일은-토요일-다음-주-월요일은-공휴일">내일은 토요일, 다음 주 월요일은 공휴일</h3>
<p>아마 나름대로 열심히 살기 2일차는 다음 주 화요일이 되지 않을까 싶다. 물론 아닐 수도 있다. 주말은 적당히 살고 다음 주 화요일부터 열심히 살도록 노력해야겠다. 인생 결국 자기만족..</p>
]]></description>
        </item>
    </channel>
</rss>