<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>1日も早くなれるじゃん。</title>
        <link>https://velog.io/</link>
        <description>'하루를 참고 인내하면 열흘을 벌 수 있고 사흘을 참고 견디면 30일을, 30일을 견디면 3년을 벌 수 있다.'</description>
        <lastBuildDate>Fri, 19 Apr 2024 15:50:57 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>1日も早くなれるじゃん。</title>
            <url>https://images.velog.io/images/dev_shu/profile/4ae4b5eb-4905-4348-911b-3470efb15232/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 1日も早くなれるじゃん。. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev_shu" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[docker-compose로 wordpress 설치]]></title>
            <link>https://velog.io/@dev_shu/docker-compose%EB%A1%9C-wordpress-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@dev_shu/docker-compose%EB%A1%9C-wordpress-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Fri, 19 Apr 2024 15:50:57 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>EC2서버에 docker-compose를 사용하여 워드프레스 설치</p>
</blockquote>
<p>이번은 docker-compose를 사용하여 EC2서버에 워드프레스를 설치해 볼거다.</p>
<ol>
<li>도커 설치 및 서비스 시작<pre><code>$ sudo yum install -y docker
$ sudo service docker start
or
$ sudo systemctl start docker.service</code></pre></li>
<li>docker-compose 설치 및 권한, 심볼릭 링크 설정<pre><code>$ sudo curl -L &quot;https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose&quot;
$ sudo chmod +x /usr/local/bin/docker-compose
$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
# docker 설치 후 /var/run/docker.sock의 permission denied 발생하는 경우
파일에 권한 변경
$ sudo chmod 666 /var/run/docker.sock
# 이제 유저도 docker 명령어 사용가능</code></pre></li>
<li>ec2서버 사용자 홈디렉토리에 docker-compose.yml 작성<pre><code>$ nano ./docker-compose.yml</code></pre></li>
</ol>
<ul>
<li><p>docker-compose.yml 내용</p>
<pre><code class="language-yml">version: &#39;3.1&#39;
services:
wordpress:
  image: wordpress
  restart: always
  ports:
    - 8080:80
  environment:
    WORDPRESS_DB_HOST: db
    WORDPRESS_DB_USER: exampleuser
    WORDPRESS_DB_PASSWORD: examplepass
    WORDPRESS_DB_NAME: exampledb

db:
  image: mysql:5.7
  restart: always
  environment:
    MYSQL_DATABASE: exampledb
    MYSQL_USER: exampleuser
    MYSQL_PASSWORD: examplepass
    MYSQL_RANDOM_ROOT_PASSWORD: &#39;1&#39;</code></pre>
</li>
</ul>
<ol start="4">
<li>작성한 파일을 통해 도커에 설치<pre><code>$ sudo docker-compose up -d</code></pre></li>
<li>ec2 서버에 보안그룹에 인바운드 규칙에 8080포트 추가
<img src="https://velog.velcdn.com/images/dev_shu/post/d880abad-4131-491f-8748-df9d34ce4d15/image.png" alt=""></li>
<li>다시 ec2서버의 주소로 들어가면 워드프레스 설치페이지로 들어가게 된다.
<img src="https://velog.velcdn.com/images/dev_shu/post/5b34187f-734d-4f6f-a330-a75162d9143e/image.png" alt=""></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Putty 설치 및 aws ec2 접속]]></title>
            <link>https://velog.io/@dev_shu/Putty-%EC%84%A4%EC%B9%98-%EB%B0%8F-aws-ec2-%EC%A0%91%EC%86%8D</link>
            <guid>https://velog.io/@dev_shu/Putty-%EC%84%A4%EC%B9%98-%EB%B0%8F-aws-ec2-%EC%A0%91%EC%86%8D</guid>
            <pubDate>Mon, 11 Mar 2024 13:54:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Mac OS에서 Putty 설치 및 ec2 접속</p>
</blockquote>
<p>OS: Monteray</p>
<ol>
<li>homebrew에 Mac PuTTY 설치</li>
</ol>
<p>homebrew가 설치 되었다면 터미널에서 </p>
<pre><code>~% brew install putty</code></pre><ol start="2">
<li><p>macport설치
macport 다운로드 접속 후 자신의 OS버전에 맞는 macport를 <a href="https://www.macports.org/install.php">다운로드</a> 및 설치.</p>
</li>
<li><p>설치 완료 후 터미널에 </p>
<pre><code>~% sudo port -v selfupdate</code></pre><p>명령어를 입력.</p>
</li>
<li><p>macport에 putty 설치</p>
<pre><code>~% sudo port install putty
~% continue? (Y)
...
...
python39 has the following notes:
 To make this the default Python or Python 3 (i.e., the version run by the
 &#39;python&#39; or &#39;python3&#39; commands), run one or both of:

     sudo port select --set python python312    &lt;-
     sudo port select --set python3 python312 &lt;-
~% sudo port select --set python python312
~% sudo port select --set python3 python312
...</code></pre></li>
<li><p>XQuartz.</p>
</li>
</ol>
<ul>
<li>XQuartz(<a href="https://www.xquartz.org/">다운로드</a>)</li>
<li>설치 후 터미널에 &#39;putty&#39; 입력</li>
</ul>
<ol start="6">
<li>AWS EC2 접속</li>
</ol>
<ul>
<li>접속 전 aws ec2서버 만들 때 받은 pem파일을 ppk파일로 puttygen 명령어를 통해 변환<pre><code>~% puttygen 파일명.pem -o 파일명.ppk</code></pre><img src="https://velog.velcdn.com/images/dev_shu/post/2931a55a-14cd-4062-9fd0-c8a17499bdd3/image.png" alt=""></li>
<li>호스트 네임란에 ec2서버에 퍼블릭 ip주소 입력</li>
<li>Connection &gt; SSG &gt; Auth &gt; Credentials에서
&#39;Private key file for authentication&#39;의 &#39;Browse...&#39;버튼 클릭</li>
<li>해당하는 암호키 파일 선택
<img src="https://velog.velcdn.com/images/dev_shu/post/baff304e-6e02-4f2a-bf24-c01a601cb964/image.png" alt=""></li>
<li>&#39;Open&#39; 버튼 클릭
<img src="https://velog.velcdn.com/images/dev_shu/post/d6f52b27-f669-4c5c-9107-be3ec7311a52/image.png" alt=""></li>
<li>&#39;Accept&#39; 버튼 클릭</li>
<li>&#39;ec2-user&#39;로 로그인
<img src="https://velog.velcdn.com/images/dev_shu/post/f2461ee4-5c75-4cc6-81b0-fe901fa4bb84/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[다이얼로그 위젯 응용]]></title>
            <link>https://velog.io/@dev_shu/%EB%8B%A4%EC%9D%B4%EC%96%BC%EB%A1%9C%EA%B7%B8-%EC%9C%84%EC%A0%AF-%EC%9D%91%EC%9A%A9</link>
            <guid>https://velog.io/@dev_shu/%EB%8B%A4%EC%9D%B4%EC%96%BC%EB%A1%9C%EA%B7%B8-%EC%9C%84%EC%A0%AF-%EC%9D%91%EC%9A%A9</guid>
            <pubDate>Thu, 29 Feb 2024 10:35:06 GMT</pubDate>
            <description><![CDATA[<ol>
<li>파일 읽고 쓰기
1-1)파일 쓰기<pre><code class="language-py">f = open(&quot;새파일.txt&quot;, &#39;w&#39;, encoding=&#39;UTF8&#39;)
f.close()</code></pre>
</li>
</ol>
<ul>
<li>파일 접근 모드</li>
</ul>
<table>
<thead>
<tr>
<th align="center">파일접근모드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">r</td>
<td>읽기모드 - 파일을 읽기만 할때</td>
</tr>
<tr>
<td align="center">w</td>
<td>쓰기모드 - 파일에 내용을 쓸 때 사용, 기존 내용 삭제하고 다시 작성</td>
</tr>
<tr>
<td align="center">a</td>
<td>추가모드 - 파일의 마지막에 새로운 내용을 추가할 때 사용</td>
</tr>
<tr>
<td align="center">- 코드</td>
<td></td>
</tr>
<tr>
<td align="center">```py</td>
<td></td>
</tr>
<tr>
<td align="center">f = open(&quot;새파일.txt&quot;, &#39;w&#39;, encoding = &#39;UTF8&#39;)</td>
<td></td>
</tr>
<tr>
<td align="center">data = &quot;안녕하세요&quot;</td>
<td></td>
</tr>
<tr>
<td align="center">f.write(data)</td>
<td></td>
</tr>
<tr>
<td align="center">f.close() #파일스트림은 닫지 않으면 다른 접근할 수 없기 때문에 작업이 끝나면 닫아줘야 한다.</td>
<td></td>
</tr>
<tr>
<td align="center">```</td>
<td></td>
</tr>
<tr>
<td align="center">1-2) 파일 읽기</td>
<td></td>
</tr>
<tr>
<td align="center">- 메서드</td>
<td></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>파일 읽기 메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>readline()</td>
<td>파일에 접근해서 제일 처음 한줄만 읽어서 저장하고, 커서를 다음 줄로 내리기</td>
</tr>
<tr>
<td>readlines()</td>
<td>파일의 모든 내용을 한줄씩 읽어서 한줄씩 리스트에 저장하기</td>
</tr>
<tr>
<td>read()</td>
<td>파일 내용 전체를 읽어서 하나의 문자열로 저장하기</td>
</tr>
<tr>
<td>- 코드</td>
<td></td>
</tr>
<tr>
<td>```py</td>
<td></td>
</tr>
<tr>
<td>def read_line():</td>
<td></td>
</tr>
<tr>
<td>f = open(&quot;새파일.txt&quot;, &#39;r&#39;)</td>
<td></td>
</tr>
<tr>
<td>line = f.readline()</td>
<td></td>
</tr>
<tr>
<td>print(line)</td>
<td></td>
</tr>
<tr>
<td>f.close()</td>
<td></td>
</tr>
</tbody></table>
<p>def read_lines():
    f = open(&quot;새파일.txt&quot;, &#39;r&#39;)
    lines = f.readlines()
    print(lines)
    for line in lines:
        print(line, end=&quot;&quot;)
    f.close()</p>
<p>def read_all():
    f = open(&quot;새파일.txt&quot;, &#39;r&#39;)
    line = f.read()
    print(line)
    f.close()</p>
<pre><code>
2. QFileDialog 기본 코드 설명
- 단일 파일선택
```py
from PySide6.QtWidgets import QFileDialog

dialog = QFileDialog(self)
# 단일 파일 선택모드
dialog.setFileMode(QFileDialog.ExistingFile)
dialog.setNameFilter(self.tr(&quot;Data Files(*.csv *.xls *.xlsx);; Images(*.png *.jpg);; All Files(*.*)&quot;))
dialog.setViewMode(QFileDialog.Detail)
if dialog.exec():
    fileNames = dialog.selectedFiles()</code></pre><ul>
<li>다중 파일 선택<pre><code class="language-py">from PySide6.QtWidgets import QFileDialog
</code></pre>
</li>
</ul>
<p>dialog = QFileDialog(self)</p>
<h1 id="다중-파일-선택모드">다중 파일 선택모드</h1>
<p>dialog.setFileMode(QFileDialog.ExistingFiles)
dialog.setNameFilter(self.tr(&quot;Data Files(<em>.csv *.xls *.xlsx);; Images(</em>.png <em>.jpg);; All Files(</em>.*)&quot;))
dialog.setViewMode(QFileDialog.Detail)
if dialog.exec():
    fileNames = dialog.selectedFiles()</p>
<pre><code>- 디렉토리 선택
```py
from PySide6.QtWidgets import QFileDialog

dialog = QFileDialog(self)
# 디렉토리 선택모드
dialog.setFileMode(QFileDialog.Directory)
dialog.setViewMode(QFileDialog.Detail)
if dialog.exec():
    dirName = dialog.selectedFiles()</code></pre><ol start="3">
<li>전체 코드<pre><code class="language-py">import sys
import os
from PySide6 import QtGui, QtUiTools
from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog
</code></pre>
</li>
</ol>
<p>loader = QtUiTools.QUiLoader()</p>
<p>class MainView(QMainWindow):
    def <strong>init</strong>(self):
        super().<strong>init</strong>()
        self.setupUI()</p>
<pre><code>def setupUI(self):
    global UI_MAIN
    UI_MAIN = loader.load(resource_path(&quot;file_dialog.ui&quot;))

    # 각 버튼에 이벤트 연결    
    UI_MAIN.btn_file.clicked.connect(self.openfile)
    UI_MAIN.btn_files.clicked.connect(self.openfiles)
    UI_MAIN.btn_dir.clicked.connect(self.opendir)
    UI_MAIN.btn_read.clicked.connect(readfile)
    UI_MAIN.btn_write.clicked.connect(writefile)

    self.setCentralWidget(UI_MAIN)
    self.setWindowTitle(&quot;파일 읽고 쓰기&quot;)
    self.setWindowIcon(QtGui.QPixmap(resource_path(&quot;python_logo.png&quot;)))
    self.resize(600, 500)
    self.show()

def openfile(self):
    UI_MAIN.te_path.clear()
    dialog = QFileDialog(self)
    # 단일 파일 선택모드
    dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
    dialog.setNameFilter(self.tr(&quot;Data Files(*.csv *.xls *.xlsx);; Images(*.png *.jpg);; All Files(*.*)&quot;))
    dialog.setViewMode(QFileDialog.ViewMode.Detail)
    if dialog.exec():
        fileName = dialog.selectedFiles()
        # line editor의 경우 setText()메서드를 썼으나, text editor의 경우 insertPlainText()메서를 사용함.
        UI_MAIN.te_path.insertPlainText(str(fileName))

def openfiles(self):
    UI_MAIN.te_path.clear()
    dialog = QFileDialog(self)
    # 다중 파일 선택모드
    dialog.setFileMode(QFileDialog.FileMode.ExistingFiles)
    dialog.setNameFilter(self.tr(&quot;Data Files(*.csv *.xls *.xlsx);; Images(*.png *.jpg);; All Files(*.*)&quot;))
    dialog.setViewMode(QFileDialog.ViewMode.Detail)
    if dialog.exec():
        fileNames = dialog.selectedFiles()
        # line editor의 경우 setText()메서드를 썼으나, 
        # text editor의 경우 insertPlainText()메서를 사용함.
        UI_MAIN.te_path.insertPlainText(str(fileNames))

def opendir(self):
    UI_MAIN.te_path.clear()
    dialog = QFileDialog(self)
    # 디렉토리 선택모드
    dialog.setFileMode(QFileDialog.Directory)
    dialog.setViewMode(QFileDialog.Detail)
    if dialog.exec():
        dirName = dialog.selectedFiles()
        UI_MAIN.te_path.insertPlainText(str(dirName))</code></pre><p>def readfile():
    path = eval(UI_MAIN.te_path.toPlainText())
    f = open(path[0], &#39;r&#39;, encoding=&#39;UTF8&#39;)
    lines = f.readlines()
    for line in lines:
        UI_MAIN.te_contents.insertPlainText(line)
    f.close()</p>
<p>def writefile():
    # toPlainText()는 읽어오는
    path = eval(UI_MAIN.te_path.toPlainText())
    f = open(path[0], &#39;w&#39;, encoding=&#39;UTF8&#39;)
    data = UI_MAIN.te_contents.toPlainText()
    f.write(data)
    f.close()
    # 파일 안에 작업이 끝난후 te_contents 내용을 지운다.
    UI_MAIN.te_contents.clear()</p>
<p>def resource_path(rel_path):
    if hasattr(sys, &quot;_MEIPASS&quot;):
        return os.path.join(sys._MEIPASS, rel_path)
    return os.path.join(os.path.abspath(&quot;.&quot;), rel_path)</p>
<p>if <strong>name</strong> == &#39;<strong>main</strong>&#39;:
    app = QApplication(sys.argv)
    main = MainView()
    sys.exit(app.exec())</p>
<pre><code>3. 실행화면
- 기본화면
![](https://velog.velcdn.com/images/dev_shu/post/5a6e2e06-58f8-4d0a-a310-aa1cad6ded75/image.png)
- 단일 파일 선택 버튼 클릭시 화면
![](https://velog.velcdn.com/images/dev_shu/post/bbd892ca-e7fe-41c6-8e60-f5a608970b56/image.png)
- 파일 선택 후 화면
![](https://velog.velcdn.com/images/dev_shu/post/c7d09d24-5725-4df1-9629-b53713d9f712/image.png)
- 읽어오기 버튼 클릭 후 화면
![](https://velog.velcdn.com/images/dev_shu/post/13866a6e-4e9b-4180-a43c-9463dd9e34f8/image.png)
- 내용 작성후 쓰기 버튼 클릭
![](https://velog.velcdn.com/images/dev_shu/post/f7280933-e27e-46c6-be6d-850f90c4dfec/image.png)
![](https://velog.velcdn.com/images/dev_shu/post/5c24f8a4-48a1-4ff2-a6fe-f43d5fcbabc0/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[응용프로그램 개발연습]]></title>
            <link>https://velog.io/@dev_shu/%EC%9D%91%EC%9A%A9%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EA%B0%9C%EB%B0%9C%EC%97%B0%EC%8A%B5</link>
            <guid>https://velog.io/@dev_shu/%EC%9D%91%EC%9A%A9%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EA%B0%9C%EB%B0%9C%EC%97%B0%EC%8A%B5</guid>
            <pubDate>Thu, 29 Feb 2024 10:33:37 GMT</pubDate>
            <description><![CDATA[<p>날짜별로 메모를 저장하는 프로그램(캘린더)</p>
<ol>
<li>UI 만들기
<img src="https://velog.velcdn.com/images/dev_shu/post/80c16c37-223e-4d17-a02d-65abde0e48b5/image.png" alt=""></li>
</ol>
<ul>
<li>맥으로 만든 UI
<img src="https://velog.velcdn.com/images/dev_shu/post/722d9f01-b9c6-46c1-b014-d35832c642c7/image.png" alt=""></li>
</ul>
<ol start="2">
<li>프로그램이 실행되면 파이썬 프로그램이 있는 위치에 note 폴더를 만든다.<pre><code class="language-py"># 디렉토리 생성하는 함수
def make_dir():
 os.makedirs(resource_path(&quot;note&quot;), exist_ok = True)</code></pre>
</li>
<li>프로그램이 실행되면 오늘 날짜에 해당하는 노트를 note폴더에서 불러온다.<pre><code class="language-py"># 해당하는 파일을 읽어서 리턴
def read_memo(filename):
 is_file = os.path.isfile(resource_path(&quot;note/&quot; + filename))
 if not is_file: return []
 f = open(resource_path(&quot;note/&quot; + filename), &#39;r&#39;)
 lines = f.readlines()
 f.close()
 return lines

</code></pre>
</li>
</ol>
<p>def date_change_str(calendar_date):
    return calendar_date.toString(&quot;yyyy-MM-dd&quot;)</p>
<h1 id="해당하는-메모-보여주는-함수">해당하는 메모 보여주는 함수</h1>
<p>def show_memo(input_date):
    # text edit에 기존 내용에 있다면 삭제
    if UI_MAIN.te_memo: UI_MAIN.te_memo.clear()</p>
<pre><code>filename = date_change_str(input_date) + &quot;.txt&quot;
memos = read_memo(filename)

for memo in memos:
    UI_MAIN.te_memo.insertPlainText(memo)</code></pre><pre><code>![](https://velog.velcdn.com/images/dev_shu/post/80d56292-c1c8-4993-b637-4c9758e38e9b/image.png)
4. 달력위젯을 클릭하면 해당하는 날짜의 노트를 불러온다.
- 위의 코드로 동시에 해결
![](https://velog.velcdn.com/images/dev_shu/post/d273463d-4cd1-40dc-bc35-638730a94dc3/image.png)
5. 노트에 내용을 입력하고 저장하면 그 날짜에 해당하는 파일 이름의 txt파일이 note폴더에 저장된다.
```py
# 파일 저장하는 함수
def save_file(memos, filename):
    f = open(&quot;note/&quot; + filename, &#39;w&#39;, encoding=&quot;UTF8&quot;)
    f.write(memos)
    f.close()


# 메모 저장하는 함수
def save_memo():
    # 메모에 저장된 내용 가져오기
    memos = UI_MAIN.te_memo.toPlainText()
    # 캘린더위젯에 선택된 날짜를 가져와서 파일명 만들기
    str_date = UI_MAIN.calendar.selectedDate().toString(&quot;yyyy-MM-dd&quot;)
    filename = str_date + &quot;.txt&quot;
    # 파일 저장
    save_file(memos, filename)</code></pre><p><img src="https://velog.velcdn.com/images/dev_shu/post/084e8583-c2a3-400b-abc0-f91c285ef071/image.png" alt="">
<img src="https://velog.velcdn.com/images/dev_shu/post/272aea44-5517-4b27-a7ca-d50d5c951ac3/image.png" alt="">
6. 전체 소스 코드</p>
<pre><code class="language-py">import sys
import os
from PySide6 import QtUiTools, QtGui
from PySide6.QtWidgets import QApplication, QMainWindow, QCalendarWidget
from datetime import datetime as dt

loader = QtUiTools.QUiLoader()


class MainView(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setupUI()

    def setupUI(self):
        global UI_MAIN
        UI_MAIN = loader.load(resource_path(&quot;calendar.ui&quot;))
        # 프로그램 실행시 note 폴더 생성
        make_dir()
        # 오늘 메모 불러오기
        today = UI_MAIN.calendar.selectedDate()
        show_memo(today)
        # 날짜를 선택했을때 이벤트
        UI_MAIN.calendar.clicked.connect(show_memo)
        # 해당 날짜에 메모 저장하는 이벤트
        UI_MAIN.btn_save.clicked.connect(save_memo)

        self.setCentralWidget(UI_MAIN)
        self.setWindowTitle(&quot;날짜별 메모 프로그램&quot;)
        # self.setWindowIcon(QtGui.QPixmap(resource_path(&quot;ani.jpg&quot;)))
        self.resize(600, 500)
        self.show()


# 디렉토리 생성
def make_dir():
    os.makedirs(resource_path(&quot;note&quot;), exist_ok=True)


# 해당하는 파일을 읽어서 리턴
def read_memo(filename):
    is_file = os.path.isfile(resource_path(&quot;note/&quot; + filename))
    if not is_file: return []
    f = open(resource_path(&quot;note/&quot; + filename), &#39;r&#39;)
    lines = f.readlines()
    f.close()
    return lines


def date_change_str(calendar_date):
    return calendar_date.toString(&quot;yyyy-MM-dd&quot;)


# 해당하는 메모 보여주는 함수
def show_memo(input_date):
    # text edit에 기존 내용에 있다면 삭제
    if UI_MAIN.te_memo: UI_MAIN.te_memo.clear()

    filename = date_change_str(input_date) + &quot;.txt&quot;
    memos = read_memo(filename)

    for memo in memos:
        UI_MAIN.te_memo.insertPlainText(memo)


# 파일 저장하는 함수
def save_file(memos, filename):
    f = open(&quot;note/&quot; + filename, &#39;w&#39;, encoding=&quot;UTF8&quot;)
    f.write(memos)
    f.close()


# 메모 저장하는 함수
def save_memo():
    # 메모에 저장된 내용 가져오기
    memos = UI_MAIN.te_memo.toPlainText()
    # 캘린더위젯에 선택된 날짜를 가져와서 파일명 만들기
    str_date = UI_MAIN.calendar.selectedDate().toString(&quot;yyyy-MM-dd&quot;)
    filename = str_date + &quot;.txt&quot;
    # 파일 저장
    save_file(memos, filename)


# 파일경로
# pyinstaller로 원파일로 압축할때 경로 필요함
def resource_path(rel_path):
    if hasattr(sys, &#39;_MEIPASS&#39;):
        return os.path.join(sys._MEIPASS, rel_path)
    return os.path.join(os.path.abspath(&quot;.&quot;), rel_path)


if __name__ == &#39;__main__&#39;:
    app = QApplication(sys.argv)
    main = MainView()
    # main.show()
    sys.exit(app.exec())</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[콤보박스 위젯 응용]]></title>
            <link>https://velog.io/@dev_shu/%EC%BD%A4%EB%B3%B4%EB%B0%95%EC%8A%A4-%EC%9C%84%EC%A0%AF-%EC%9D%91%EC%9A%A9</link>
            <guid>https://velog.io/@dev_shu/%EC%BD%A4%EB%B3%B4%EB%B0%95%EC%8A%A4-%EC%9C%84%EC%A0%AF-%EC%9D%91%EC%9A%A9</guid>
            <pubDate>Thu, 29 Feb 2024 10:32:32 GMT</pubDate>
            <description><![CDATA[<ol>
<li>QComboBox UI
<img src="https://velog.velcdn.com/images/dev_shu/post/aef91298-0c0b-4f9c-a6ee-9f0d898e7536/image.png" alt=""></li>
<li>메서드</li>
</ol>
<ul>
<li>아이템 입력하기<blockquote>
</blockquote>
addItem(icon, text, userData=None)
icon, userData는 생략 가능
ex) cbox = QComboBox()
  cbox.addItem(&quot;서울특별시&quot;)</li>
<li>아이템 추가하기<blockquote>
<p>insertItem(index, icon, text, userData=None)</p>
</blockquote>
</li>
<li>콤보박스에 현재 선택된 아이템 값 받기<blockquote>
<p>currentIndex(): 현재 선택된 아이템의 인덱스 반환
currentText(): 현재 선택된 아이템의 텍스트 반환
currentData(): 현재 선택된 아이템의 데이터 반환</p>
</blockquote>
</li>
<li>콤보박스의 현재 선택된 값 변경<blockquote>
<p>setCurrentIndex(숫자): 해당 숫자의 인덱스로 변경
setCurrentText(&quot;경기도&quot;)</p>
</blockquote>
</li>
<li>삭제하기<blockquote>
<p>전체 삭제: clear()
한개 삭제: removeItem(해당 인덱스)</p>
</blockquote>
</li>
<li>전체 개수 추출<blockquote>
<p>count(): 해당 콤보박스의 아이템 전체 갯수 반환.</p>
</blockquote>
</li>
<li>이벤트 연결<blockquote>
<p>currentIndexChanged.connect(func): 콤보박스의 변경 있을 시 해당 함수 실행</p>
</blockquote>
</li>
</ul>
<ol start="3">
<li>전체 소스 코드<pre><code class="language-py">import sys
import os
from PySide6 import QtUiTools, QtGui
from PySide6.QtWidgets import QApplication, QMainWindow
</code></pre>
</li>
</ol>
<h1 id="combobox는-따로-임포트-할-필요없다">combobox는 따로 임포트 할 필요없다.</h1>
<p>loader = QtUiTools.QUiLoader()</p>
<p>class MainView(QMainWindow):
    def <strong>init</strong>(self):
        super().<strong>init</strong>()
        self.setupUI()</p>
<pre><code>def setupUI(self):
    global UI_MAIN
    UI_MAIN = loader.load(resource_path(&quot;combobox.ui&quot;))

    getCnt()
    UI_MAIN.cb_area.currentIndexChanged.connect(getComboBox)
    UI_MAIN.btn_input.clicked.connect(inputComboBox)
    UI_MAIN.btn_del.clicked.connect(deleteComboBox)
    UI_MAIN.btn_clear.clicked.connect(clearComboBox)
    UI_MAIN.btn_all.clicked.connect(getAll)

    self.setCentralWidget(UI_MAIN)
    self.setWindowTitle(&quot;QComboBox&quot;)
    self.setWindowIcon(QtGui.QPixmap(resource_path(&quot;ani.jpg&quot;)))
    self.resize(600, 500)
    self.show()</code></pre><h1 id="콤보박스-이벤트-발생시">콤보박스 이벤트 발생시</h1>
<p>def getComboBox():
    # if UI_MAIN.te_result.toPlainText():
    #     UI_MAIN.te_result.clear()
    idx = UI_MAIN.cb_area.currentIndex()
    text = UI_MAIN.cb_area.currentText()
    data = UI_MAIN.cb_area.currentData()</p>
<pre><code>UI_MAIN.te_result.append(str(idx) + &quot; &quot; + text + &quot; &quot; + str(data))</code></pre><h1 id="입력하기">입력하기</h1>
<p>def inputComboBox():
    text = UI_MAIN.le_txt.text()
    data = UI_MAIN.le_data.text()</p>
<pre><code>UI_MAIN.cb_area.addItem(text, data)
getCnt()</code></pre><h1 id="선택삭제">선택삭제</h1>
<p>def deleteComboBox():
    idx = UI_MAIN.cb_area.currentIndex()</p>
<pre><code>UI_MAIN.cb_area.removeItem(idx)
getCnt()
getAll()</code></pre><h1 id="전체-삭제">전체 삭제</h1>
<p>def clearComboBox():
    UI_MAIN.cb_area.clear()
    getCnt()
    getAll()</p>
<h1 id="전체보기">전체보기</h1>
<p>def getAll():
    UI_MAIN.te_result.clear()
    cnt = UI_MAIN.cb_area.count()
    # 현재 선택된 콤보박스의 인덱스가 첫번째인 경우
    if UI_MAIN.cb_area.currentIndex() == 0:
        idx = UI_MAIN.cb_area.currentIndex()
        text = UI_MAIN.cb_area.currentText()
        data = UI_MAIN.cb_area.currentData()
        UI_MAIN.te_result.append(str(idx) + &quot; &quot; + text + &quot; &quot; + str(data))</p>
<pre><code>for i in range(cnt):
    UI_MAIN.cb_area.setCurrentIndex(i)
    # getComboBox()</code></pre><h1 id="콤보박스-아이템-전체-갯수">콤보박스 아이템 전체 갯수</h1>
<p>def getCnt():
    cnt = UI_MAIN.cb_area.count()</p>
<pre><code>UI_MAIN.le_cnt.setText(str(cnt))</code></pre><h1 id="파일경로">파일경로</h1>
<h1 id="pyinstaller로-원파일로-압축할때-경로-필요함">pyinstaller로 원파일로 압축할때 경로 필요함</h1>
<p>def resource_path(rel_path):
    if hasattr(sys, &#39;_MEIPASS&#39;):
        return os.path.join(sys._MEIPASS, rel_path)
    return os.path.join(os.path.abspath(&quot;.&quot;), rel_path)</p>
<p>if <strong>name</strong> == &#39;<strong>main</strong>&#39;:
    app = QApplication(sys.argv)
    main = MainView()
    # main.show()
    sys.exit(app.exec())</p>
<pre><code>4. 실행화면
- 전체보기 버튼 클릭시
![](https://velog.velcdn.com/images/dev_shu/post/0014327e-5953-43e7-840c-53260bacb5de/image.png)
- 선택삭제 버튼 클릭시
![](https://velog.velcdn.com/images/dev_shu/post/a87d3aa3-9301-4bbf-b4a9-b1b61f130f54/image.png)
- 전체삭제 버튼 클릭시
![](https://velog.velcdn.com/images/dev_shu/post/e76fcf37-6d9b-4f11-b2e9-93ab3756a337/image.png)
- 입력버튼 클릭시
![](https://velog.velcdn.com/images/dev_shu/post/9be58994-a3f4-4e7b-8132-0b923e218a02/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[메시지 위젯 응용]]></title>
            <link>https://velog.io/@dev_shu/%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%9C%84%EC%A0%AF-%EC%9D%91%EC%9A%A9</link>
            <guid>https://velog.io/@dev_shu/%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%9C%84%EC%A0%AF-%EC%9D%91%EC%9A%A9</guid>
            <pubDate>Thu, 29 Feb 2024 10:30:27 GMT</pubDate>
            <description><![CDATA[<ol>
<li>QMessageBox 기본 화면과 기본 코드
1-1) 기본 화면
<img src="https://velog.velcdn.com/images/dev_shu/post/761546be-6ac9-4513-8d84-7369ec80b5f0/image.png" alt="">
1-2) 기본 코드<pre><code class="language-py">from PySide6.QtWidgets import QMessageBox

</code></pre>
</li>
</ol>
<h1 id="qmessageboxfmf-msg_box로-저장-필요한-내용들-세팅">QMessageBoxfmf msg_box로 저장, 필요한 내용들 세팅</h1>
<p>msg_box = QMessageBox()
msg_box.setWinddowTitle(&quot;알림창&quot;) # 메시지창의 상단 제목
msg_box.setWindowIcon(QtGui.QPixmap(&quot;info.png&quot;)) # 메시지창 상단 제목의 아이콘
msg_box.setIcon(QMessageBox.Icon.Information) # 메시지창 내부에 표시될 아이콘
msg_box.setText(&quot;This is title&quot;)
msg_box.setInformativeText(&quot;This is content&quot;)
msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No | QMessageBox.StandardButton.Cancel) # 메시지창의 버튼
msg_box.setDefaultButton(QMessageBox.StandardButton.Yes) # Yes버튼 포커스</p>
<p>msg_box.exec() # 클릭한 버튼 결과 리턴</p>
<pre><code>- 맥의 경우, 가이드라인에 따라 메시지 박스의 윈도우 타이틀과 아이콘은 보이지 않는다.
![](https://velog.velcdn.com/images/dev_shu/post/83fa954f-cb53-466a-9c2a-d896fa7e3a5f/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[기본 위젯 응용]]></title>
            <link>https://velog.io/@dev_shu/%EA%B8%B0%EB%B3%B8-%EC%9C%84%EC%A0%AF-%EC%9D%91%EC%9A%A9</link>
            <guid>https://velog.io/@dev_shu/%EA%B8%B0%EB%B3%B8-%EC%9C%84%EC%A0%AF-%EC%9D%91%EC%9A%A9</guid>
            <pubDate>Thu, 29 Feb 2024 10:28:31 GMT</pubDate>
            <description><![CDATA[<p>1.UI 파일 생성</p>
<ul>
<li>이번에 만들어 볼 UI
<img src="https://velog.velcdn.com/images/dev_shu/post/7087e065-e187-4dc8-ba2f-a25f1909cda0/image.png" alt="">
1-1) 레이아웃과 위젯 배치
<img src="https://velog.velcdn.com/images/dev_shu/post/d8a21567-9c6c-4d2a-bdf9-468ec4e37fd5/image.png" alt=""></li>
<li>이름, 나이, 클릭버튼 부분 horizontal layout으로 배치
<img src="https://velog.velcdn.com/images/dev_shu/post/6f10c03e-3d0b-4358-abfc-4ef88b5ba056/image.png" alt=""></li>
<li>이름, 나이 레이아웃 부분에 Text Label, Line Edit 배치</li>
<li>클릭 버튼 레이아웃엔 Push Button 배치</li>
<li>클릭 버튼 레이아웃 아래 부분에 Text Label 배치
1-2) 속성 편집
<img src="https://velog.velcdn.com/images/dev_shu/post/411b3118-a02e-4863-9520-2772bbd708c1/image.png" alt=""></li>
<li>해당 Text Label에 &#39;이름&#39;, &#39;나이&#39;, &#39;결과&#39;로 변경하고 Push Button도 &#39;Click&#39;으로 변경</li>
<li>해당 부분을 더블 클릭해서 변경하거나 속성편집기에서 변경가능
<img src="https://velog.velcdn.com/images/dev_shu/post/a8f38dbf-d8f4-4305-bcbc-d9a60331f99c/image.png" alt=""></li>
<li>각 속성에 나온 대로 변경
<img src="https://velog.velcdn.com/images/dev_shu/post/9110b933-f014-4339-824d-8ec23ab0d5ff/image.png" alt=""></li>
<li>변경 후 UI
<img src="https://velog.velcdn.com/images/dev_shu/post/db826eb8-e498-4994-a4d5-d02a8d207fba/image.png" alt=""></li>
<li>크기 조정시 자동으로 조정되기 위해 central widget을
Vertical Layout으로 변경 후 최종 UI
<img src="https://velog.velcdn.com/images/dev_shu/post/79fbeba4-c2d3-4b45-93cb-9f9fbbe593b4/image.png" alt="">
1-3) 소스<pre><code class="language-py">import sys
import os
import re
from PySide6 import QtUiTools, QtGui
from PySide6.QtWidgets import QApplication, QMainWindow
</code></pre>
</li>
</ul>
<p>loader = QtUiTools.QUiLoader()</p>
<p>class MainView(QMainWindow):
    def <strong>init</strong>(self):
        super().<strong>init</strong>()
        self.setupUI()</p>
<pre><code>def setupUI(self):
    global UI_MAIN
    UI_MAIN = loader.load(resource_path(&quot;btnTest.ui&quot;))
    # 이름 라인에디트를 입력했을때 이벤트
    UI_MAIN.lineEdit_name.textChanged.connect(checkName)
    # 나이 라인에디트를 입력했을 이벤트
    UI_MAIN.lineEdit_age.textChanged.connect(checkAge)
    # Click 버튼을 클릭했을때
    UI_MAIN.pushButton_click.clicked.connect(getResult)

    self.setCentralWidget(UI_MAIN)
    self.setWindowTitle(&quot;BUTTON_TEST&quot;)
    self.setWindowIcon(QtGui.QPixmap(resource_path(&quot;ani.jpg&quot;)))
    self.resize(500, 300)
    self.show()</code></pre><h1 id="라인-에디터의-내용을-가져와서-결과라벨에-출력">라인 에디터의 내용을 가져와서 결과라벨에 출력</h1>
<p>def getResult():
    user_name = UI_MAIN.lineEdit_name.text()
    user_age = UI_MAIN.lineEdit_age.text()</p>
<pre><code>result = user_name + &quot;님의 나이는 &quot; + user_age + &quot;살입니다.&quot;
UI_MAIN.label_result.setText(result)</code></pre><h1 id="이름-라인에디트의-글자만-입력하게-체크하는-함수">이름 라인에디트의 글자만 입력하게 체크하는 함수</h1>
<p>def checkName(data):
    name = data if data.isalpha() else data[:-1]
    UI_MAIN.lineEdit_name.setText(name)</p>
<h1 id="나이-라인-에디트의-숫자만-입력하게-체크하는-함수">나이 라인 에디트의 숫자만 입력하게 체크하는 함수</h1>
<p>def checkAge(data):
    # 매개변수 input_age가 숫자인 경우 age 변수에 대입, 숫자가 아닌 경우 입력된 문자를 제거
    try:
        int(data)
        age = data
    except:
        # 변경시마다 발생하는 이벤트이기에, 숫자 외에 다른 문자가나 기호가 입력될시 실행되는 부분
        age = data[:-1]</p>
<pre><code>UI_MAIN.lineEdit_age.setText(age)</code></pre><h1 id="파일경로">파일경로</h1>
<h1 id="pyinstaller로-원파일로-압축할때-경로-필요함">pyinstaller로 원파일로 압축할때 경로 필요함</h1>
<p>def resource_path(rel_path):
    if hasattr(sys, &#39;_MEIPASS&#39;):
        return os.path.join(sys._MEIPASS, rel_path)
    return os.path.join(os.path.abspath(&quot;.&quot;), rel_path)</p>
<p>if <strong>name</strong> == &#39;<strong>main</strong>&#39;:
    app = QApplication(sys.argv)
    main = MainView()
    # main.show()
    sys.exit(app.exec())</p>
<pre><code>- checkName 함수의 경우 isalpha()가 특수 문자도 문자로 포함하기에 정규식을 통해서 더 간단하게 수정, checkAge 함수도 같이 변경
```py
def checkName(data):    
    # 정규 표현식을 사용해서 한글과 영어만 들어오게
    # \uAC00-\uD7A3 : 한글, a-zA-Z: 영어 대소문자
    name = re.sub(r&quot;[^\uAC00-\uD7A3a-zA-Z\s]&quot;, &quot;&quot;, data)
    UI_MAIN.lineEdit_name.setText(name)


def checkAge(data):
    age = re.sub(r&quot;[^0-9]&quot;, &quot;&quot;, data)
    UI_MAIN.lineEdit_age.setText(age)</code></pre><ul>
<li>여기서 좀 더 간단하게 공통적인 부분을 빼서 코드를 작성<pre><code class="language-py">def checkName(data):
  check(data, &quot;name&quot;)

</code></pre>
</li>
</ul>
<p>def checkAge(data):
    check(data, &quot;age&quot;)</p>
<p>def check(data, types):
    if types == &quot;name&quot;:
        name = re.sub(r&quot;[^\uAC00-\uD7A3a-zA-Z\s]&quot;, &quot;&quot;, data)
        UI_MAIN.lineEdit_name.setText(name)
    else:
        age = re.sub(r&quot;[^0-9]&quot;, &quot;&quot;, data)
        UI_MAIN.lineEdit_age.setText(age)</p>
<pre><code>- 여기서 클로저 함수를 쓰면 함수 3개를 함수 1개로 줄일 수 있다.
```py
# 위의 코드부분도 변경
# 이름 라인에디트를 입력했을때 이벤트
UI_MAIN.lineEdit_name.textChanged.connect(check(&quot;name&quot;))
# 나이 라인에디트를 입력했을 이벤트   
UI_MAIN.lineEdit_age.textChanged.connect(check(&quot;age&quot;))


def check(send_type):
    def check_type(data):
        if send_type == &quot;name&quot;:
            chk_name = re.sub(r&quot;[^\uAC00-\uD7A3a-zA-Z\s]&quot;, &quot;&quot;, data)
            UI_MAIN.lineEdit_name.setText(chk_name)
        else:
            chk_age = re.sub(r&quot;[^0-9]&quot;, &quot;&quot;, data)
            UI_MAIN.lineEdit_age.setText(chk_age)
    return check_type</code></pre><ul>
<li>main.py 실행 화면
<img src="https://velog.velcdn.com/images/dev_shu/post/d5aedd72-5721-499f-8cfe-aad7ab68ef17/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Widget 기본 사용법]]></title>
            <link>https://velog.io/@dev_shu/Widget-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@dev_shu/Widget-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Tue, 27 Feb 2024 19:07:54 GMT</pubDate>
            <description><![CDATA[<ol>
<li>Widget 클래스
1-1. Widget 상자의 구성
<img src="https://velog.velcdn.com/images/dev_shu/post/e3a2909d-4756-4ea9-8f96-6ead456346e3/image.png" alt=""></li>
</ol>
<ul>
<li>Layouts: 프로그램의 전체 배치에 대한 위젯</li>
<li>Spacers: 위젯 간의 간격을 위한 위젯
<img src="https://velog.velcdn.com/images/dev_shu/post/9640932c-eb18-4a46-9e59-f9d31d8dbcfc/image.png" alt=""></li>
<li><blockquote>
<p>Horizontal Spacer: 수평 간격 위젯</p>
</blockquote>
</li>
<li><blockquote>
<p>Vertical Spacer: 수직 간격 위젯</p>
</blockquote>
</li>
<li>Buttons: 여러 가지 종류의 Button 위젯
<img src="https://velog.velcdn.com/images/dev_shu/post/16d80c07-d479-471c-a728-deb892482cba/image.png" alt=""></li>
<li>Item Views(Model-Based): DB와 연계해 보여주는 View 집합
<img src="https://velog.velcdn.com/images/dev_shu/post/6cccdf3a-47c8-4624-a17a-74a243e1e75a/image.png" alt=""></li>
<li>Item Widgets(Item-Based): 데이터의 각 값을 object로 생성해서 보여주는 위젯의 집합
<img src="https://velog.velcdn.com/images/dev_shu/post/2e865611-31bb-4e9d-8f6e-3c64fa4db726/image.png" alt=""></li>
<li>Containers: 위젯들을 그룹화 시켜주는 위젯들
<img src="https://velog.velcdn.com/images/dev_shu/post/9f83af1d-b229-4f43-a8ed-9c3aa78aa033/image.png" alt=""></li>
<li>Input Widgets: 데이터 입력을 위한 위젯들
<img src="https://velog.velcdn.com/images/dev_shu/post/31c5f03a-e1f4-44dd-bdd9-3f47848c98d2/image.png" alt=""></li>
<li>Display Widgets: 텍스트 설명 및 데이터의 상태를 보여주는 위젯들
<img src="https://velog.velcdn.com/images/dev_shu/post/24d65105-b024-41cf-802f-7c6999414a05/image.png" alt="">
1-2. QWidget 클래스의 상속관계
<img src="https://velog.velcdn.com/images/dev_shu/post/80d5ecbc-aafc-4c16-8266-4079849de2ab/image.png" alt="">
1-3. Qwidget을 상속받는 Widget들
<img src="https://velog.velcdn.com/images/dev_shu/post/951468ef-e5f3-4ffd-bb03-d3c1de215f37/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[GUI Layout]]></title>
            <link>https://velog.io/@dev_shu/GUI-Layout</link>
            <guid>https://velog.io/@dev_shu/GUI-Layout</guid>
            <pubDate>Tue, 27 Feb 2024 19:05:04 GMT</pubDate>
            <description><![CDATA[<ol>
<li>레이아웃 구조
<img src="https://velog.velcdn.com/images/dev_shu/post/97cfae21-4f51-462c-98ec-93c84c529b9a/image.png" alt=""></li>
</ol>
<ul>
<li><p>Main Window: root</p>
</li>
<li><p>Central Widget: html태그 중에 body에 해당하는 부분
<img src="https://velog.velcdn.com/images/dev_shu/post/4fb267d9-19fb-49b2-a708-c9872cb124e1/image.png" alt=""></p>
</li>
<li><p>Horizontal Layout: 레이아웃 내의 위젯들을 자동적으로 가로로 나열시킴</p>
</li>
<li><p>Vertical Layout: 레이아웃 내의 위젯들을 자동적으로 세로로 나열시킴.</p>
</li>
<li><p>Grid Layout: 레이아웃 내의 위젯들을 자동적으로 바둑판형식으로 나열시킴.</p>
</li>
<li><p>Form Layout: 레이아웃 내의 위젯들을 폼 형태로 나열함. 폼이란 가로가 2줄로 된 배열을 말함. </p>
</li>
<li><p>레이아웃 안에 레이아웃 삽입 가능</p>
</li>
<li><p>앱의 크기 조정시 레이아웃이 안 보일 수도 있어서 마지막엔 Central Widget에서 레이아웃을 지정해서 최종 정렬하면 문제 해결 가능.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[UI 생성]]></title>
            <link>https://velog.io/@dev_shu/2-UI-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@dev_shu/2-UI-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Tue, 27 Feb 2024 14:14:46 GMT</pubDate>
            <description><![CDATA[<ol>
<li>Qt Designer</li>
</ol>
<p>-&gt; PySide에 있는 gui프로그램
-&gt; 레이아웃과 버튼 등을 드래그해서 gui구성을 쉽게 할 수 있음
1-1. 실행
-&gt; 맥의 경우, 터미널에서 &#39;Designer&#39;를 입력하면 실행
-&gt; 윈도우의 경우, 설치된 경로에서 &#39;designer.exe&#39; 실행</p>
<ul>
<li><p>맥에서 실행화면
<img src="https://velog.velcdn.com/images/dev_shu/post/2c719946-2136-4c9f-bf70-75a2fb039390/image.png" alt=""></p>
</li>
<li><p>윈도우에서 실행화면
<img src="https://velog.velcdn.com/images/dev_shu/post/c29a2b4b-dbe1-47f8-8db9-42d2b6c3d7a8/image.png" alt="">
1-2. 새 폼 생성</p>
</li>
<li><p>초기화면(맥)
<img src="https://velog.velcdn.com/images/dev_shu/post/b00cd935-8285-48de-8872-d52e4235be43/image.png" alt=""></p>
</li>
<li><p>생성 버튼 클릭시 화면(MainWindow)</p>
</li>
<li><p>이 GUI에서 최상단이 되는 게 MainWindow
<img src="https://velog.velcdn.com/images/dev_shu/post/e70e1709-30bd-4839-ba49-6e1a6dbfd6fa/image.png" alt=""></p>
</li>
<li><p>MainWindow의 구성
<img src="https://velog.velcdn.com/images/dev_shu/post/5f28fa05-8a22-4490-847d-04eeb756310d/image.png" alt=""></p>
</li>
<li><p>속성편집기
<img src="https://velog.velcdn.com/images/dev_shu/post/ef80149a-96e5-4b6f-9b7f-8141b5ffa889/image.png" alt=""></p>
</li>
</ul>
<ol start="2">
<li>Python과 UI파일 연동
2-1. 소스코드<pre><code class="language-py">import sys
import os
from PySide6 import QtUiTools, QtGui
from PySide6.QtWidgets import QApplication, QMainWindow
</code></pre>
</li>
</ol>
<p>loader = QtUiTools.QUiLoader()</p>
<h1 id="gui화면-설정-관련된-클래스">gui화면 설정 관련된 클래스</h1>
<p>class MainView(QMainWindow):</p>
<h1 id="프로그래밍언어들에선-클래스를-만들-때-초기화-메서드가-들어감">프로그래밍언어들에선 클래스를 만들 때 초기화 메서드가 들어감.</h1>
<pre><code>def __init__(self):
    super().__init__()
    self.setupUI()</code></pre><h1 id="designer-앱으로-만들어진-ui파일-읽어오고">Designer 앱으로 만들어진 ui파일 읽어오고,</h1>
<h1 id="gui의-타이틀-아이콘-크기-등의-설정">gui의 타이틀, 아이콘, 크기 등의 설정</h1>
<pre><code>def setupUI(self):
    global UI_MAIN
    UI_MAIN = loader.load(resource_path(&quot;layout.ui&quot;))

    self.setCentralWidget(UI_MAIN)
    self.setWindowTitle(&quot;UI_TEST&quot;)
    self.setWindowIcon(QtGui.QPixmap(resource_path(&quot;ani.jpg&quot;)))
    self.resize(600, 500)
    self.show()</code></pre><h1 id="mainview-클래스-끝">MainView 클래스 끝</h1>
<h1 id="파일경로-함수">파일경로 함수</h1>
<h1 id="pyinstaller로-원파일로-압축할때-경로-필요함">pyinstaller로 원파일로 압축할때 경로 필요함</h1>
<h1 id="다른-컴퓨터로-실행시-경로가-다를-수-있기에-경로-설정이-필요함">다른 컴퓨터로 실행시 경로가 다를 수 있기에 경로 설정이 필요함.</h1>
<p>def resource_path(rel_path):
    if hasattr(sys, &#39;_MEIPASS&#39;):
        return os.path.join(sys._MEIPASS, rel_path)
    return os.path.join(os.path.abspath(&quot;.&quot;), rel_path)</p>
<h1 id="메인앱-실행코드">메인(앱 실행코드)</h1>
<p>if <strong>name</strong> == &#39;<strong>main</strong>&#39;:
    app = QApplication(sys.argv)
    main = MainView()
    # main.show()
    sys.exit(app.exec())</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[설치]]></title>
            <link>https://velog.io/@dev_shu/Python-PySide-PyCharm</link>
            <guid>https://velog.io/@dev_shu/Python-PySide-PyCharm</guid>
            <pubDate>Tue, 27 Feb 2024 00:19:36 GMT</pubDate>
            <description><![CDATA[<p><img src="https://blog.kakaocdn.net/dn/ddsqBx/btq7I1evkW7/KspjmKDL8onYh24zhcpniK/img.jpg" alt=""></p>
<ol>
<li>파이썬 설치
1-1. 파이썬 버전 확인<pre><code class="language-bash">#파이썬 버전확인
python3 --version</code></pre>
</li>
</ol>
<ul>
<li><p>파이썬 버전확인 시 버전이 나오지 않으면 설치가 안 되어있는 경우.</p>
</li>
<li><p>기본적으로 맥북은 파이썬2 버전이 설치되어 있음.</p>
</li>
<li><p>이번에 내가 공부해볼 건 파이썬gui이기 때문에 영상에 맞춰서 파이썬 12.2.0버전을 설치할 예정</p>
<p>1-2. 파이썬 설치(brew)</p>
<pre><code class="language-bash">#brew list로 설치된 파이썬 버전 확인해보기
brew list python
#brew search로 파이썬 버전 찾기
brew search python3.12
#설치
brew install python@3.12
#버전확인
python3 -v</code></pre>
</li>
</ul>
<ol start="2">
<li>PySide6 설치
2-1. PySide란?</li>
</ol>
<ul>
<li>Qt for Python 프로젝트의 일부로 Qt 컴퍼니에서 개발한 크로스 플랫폼 GUI 툴킷 Qt (소프트웨어)의 파이썬 바인딩.
2-2. 설치<pre><code class="language-bash">#brew search로 pyside 버전 찾기
#pyside와 pyside@2가 있는데, pyside가 최신버전.
#동영상 강의에서 버전6 설치를 원해서 pyside 혹은 pyside@6로 설치하면 됨
brew install pyside@6</code></pre>
2-3. 버전 확인<pre><code class="language-bash">vi pyside.py</code></pre>
</li>
</ul>
<pre><code class="language-py"># pyside.py 소스
import PySide6.QtCore

print(PySide6.__version__)
print(PySide6.QtCore.__version__)</code></pre>
<pre><code class="language-bash">python3 pyside6.py
6.6.2
6.6.2</code></pre>
<ol start="3">
<li>PyCharm설치(CE)
3-1. 설치<pre><code class="language-bash"># brew cask로 설치
brew install --cask pycharm-ce
# 터미널에서 실행
pycharm-ce</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[JSDoc을 이용한 타입체크]]></title>
            <link>https://velog.io/@dev_shu/JSDoc%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%83%80%EC%9E%85%EC%B2%B4%ED%81%AC</link>
            <guid>https://velog.io/@dev_shu/JSDoc%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%83%80%EC%9E%85%EC%B2%B4%ED%81%AC</guid>
            <pubDate>Mon, 21 Mar 2022 08:57:36 GMT</pubDate>
            <description><![CDATA[<h3 id="타입지정-타입체크">타입지정? 타입체크</h3>
<ul>
<li>유투브에서 여러 개발 영상을 보다가 JSDoc를 이용한 타입을 체크하는 방식이 있어서 블로깅을 합니다. 맨 처음에 타입스크립트처럼 타입지정을 해서 오류를 체크하는 방식인가 했더니, JSDoc에서 파라미터의 타입을 지정해주어 @ts-check를 통해 타입 오류에 대한 체크만 해주는 것이었다.
그래서 컴파일했을 시, 기능은 돌아가긴 한다.</li>
<li>예제코드<pre><code class="language-js">// @ts-check
// 위의 주석을 해줘야 타입에 대한 체크를 해줌
</code></pre>
</li>
</ul>
<p>// 아래와 같이 주석을 달아주면,
/**</p>
<ul>
<li>@param a {number}</li>
<li>@param b {number}</li>
<li>@returns {number}</li>
<li>/</li>
</ul>
<p>const add = (a, b) =&gt; a + b;
const a = &quot;4&quot;;
const b = 5;
console.log(add(a, b));
// 인자 a의 타입이 잘못 됐다고 에러표시를 해줌
/**</p>
<ul>
<li>두 수의 차를 구한다.</li>
<li>@type {(a: number, b:number) =&gt; number}</li>
<li>/</li>
</ul>
<p>const minus = (a, b) =&gt; a - b;
console.log(minus(&quot;3&quot;, 2));
// 위의 것도 마찬가지로 에러 표시</p>
<p>```</p>
<ul>
<li>이것 이외에도 JSDoc에 대한 알고 싶으면, <a href="https://poiemaweb.com/jsdoc-type-hint">여기</a></li>
</ul>
<h3 id="proposal-types-as-comments">proposal-types-as-comments</h3>
<ul>
<li>위의 처럼 자바스크립트도 타입스크립트처럼 타입을 지정해서 사용할수 있게 ECMA TC39에 제안이 들어왔다고 해야 되나? 고려해야 한다는 오피셜이 떴다.</li>
<li>그에 해당하는 내용은 <a href="https://github.com/giltayar/proposal-types-as-comments/">여기</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[접근제한자]]></title>
            <link>https://velog.io/@dev_shu/%EC%A0%91%EA%B7%BC%EC%A0%9C%ED%95%9C%EC%9E%90</link>
            <guid>https://velog.io/@dev_shu/%EC%A0%91%EA%B7%BC%EC%A0%9C%ED%95%9C%EC%9E%90</guid>
            <pubDate>Mon, 14 Mar 2022 04:10:26 GMT</pubDate>
            <description><![CDATA[<h3 id="접근제한자를-블로깅한-이유">접근제한자를 블로깅한 이유</h3>
<ul>
<li><p>유투브의 영상을 보며 영상 안에 코드를 보았는데, 코드 안에서
보지 못했던 문법이 있어 검색하다보니 ES2019에 접근제한자 관련된 문법이 추가되었더라.</p>
</li>
<li><p>코드</p>
<pre><code class="language-js">class Cell {
#cellElement; // 이렇게 해시를 붙여서 이 변수는 private다라고 선언
#x;
#y;
#tile;
constructor(cellElement, x, y) {
  this.#cellElement * cellElement;
  this.#x = x;
  this.#y = y;
}
get tile() {
  return this.#tile;
}
}</code></pre>
<h3 id="접근제한자">접근제한자</h3>
</li>
<li><p>다른 클래스에서 현재 클래스의 변수나 메서드의 접근 가능 여부를 선언하는 기법을 말함.</p>
</li>
<li><p>자바의 경우 변수나 메서드 앞에 public, protected, private을 키워드를 선언해서 쓰지만, 이전까지의 자바스크립트에선 따로 정의되어 있지 않았으나 &#39;#&#39;를 통해 선언 가능해짐</p>
<ul>
<li>&#39;#(해시)&#39;는 키워드라고 안하고 프리픽스라고 하는 듯.</li>
</ul>
</li>
<li><p>변수를 private으로 선언한 경우, 인스턴스를 생성했을 시 직접적으로 변수에 접근하지 못하고,
클래스의 getter(),setter()를 통해 접근 가능하다.(자바의 경우)</p>
</li>
<li><p>자바에서의 접근제한자 사용법</p>
<pre><code class="language-java">public class Data {
private String name = null;
private String season = null;
private int year = 0;

public void setName(String name) {
  this.name = name;
}

public void setSeason(String season) {
    this.season = season;
}

public void setYear(int year) {
    if(year &gt;= 20 &amp;&amp; year &lt;= 30) {
    this.year = year;
  } else {
    System.out.println(&quot;입력될 수 있는 나이는 20-30세 사이입니다.&quot;);    
  }
}

public String getName() {
    return name;
}

public String getSeason() {
    return season;
}

public int getYear() {
    return year;
}
}</code></pre>
</li>
</ul>
<h3 id="자바스크립트의-접근제한자-사용법">자바스크립트의 접근제한자 사용법</h3>
<ul>
<li><p>예전의 경우: 모듈 패턴을 이용하거나 Symbol을 사용해서 private처럼 구현함.</p>
<pre><code class="language-js">// 모듈 패턴 
  function someModule() {
    const privateProp = &#39;dont touch this&#39;;
    const publicProp = &#39;you can touch this&#39;;

    _doSomethingWithPrivateProp = () =&gt; {...}

    const publicMethod = () =&gt; {
      _doSomethingWithPrivateProp();
      //...
    }

    return {
      publicProp,
      publicMethod
    }
  }

    // Symbol
    const privateMethodName = Symbol();
    const pivatePropName = Symbol();

    class SomeClass {
      [privatePropName] = &#39;dont touch this&#39;;
        publicProp = &#39;you can touch this&#39;;

        [privateMethodName]() {
        console.log(&#39;priavte method&#39;);  
      }
      publicMethod() {
        this[privateMathodName](this[privateMethodName]);  
      }
    }</code></pre>
</li>
<li><p>ECMA2019에서 나온 &#39;#&#39;</p>
<ul>
<li>맨 처음 자바 코드를 &#39;#&#39; 프리픽스를 사용하여 자바스크립트 코드로 바꿔보기.  <pre><code class="language-js">class Data {
#name;
#season;
#year;
constructor(name, season, year) {
this.#name = name;
this.#season = season;
this.#year = year;
}
set setName(name) {
this.#name = name;
}
</code></pre>
</li>
</ul>
<p>set setSeason(season) {</p>
<pre><code>this.#season = season;</code></pre><p>}</p>
<p>set setYear(year) {</p>
<pre><code>if(year &gt;= 20 &amp;&amp; year &lt;= 30) {
this.#year = year;</code></pre><p>  } else {</p>
<pre><code>console.log(&quot;입력될 수 있는 나이는 20-30세 사이입니다.&quot;);    </code></pre><p>  }
}</p>
<p>get getName() {</p>
<pre><code>return this.#name;</code></pre><p>}</p>
<p>get getSeason() {</p>
<pre><code>return this.#season;</code></pre><p>}</p>
<p>get getYear() {</p>
<pre><code>return this.#year;</code></pre><p>}</p>
<p>getData() {
  return <code>name: ${this.#name}, year: ${this.#year}, season: ${this.#season}</code> 
}
}
const data = new Data(&#39;siwoo&#39;,&#39;spring&#39;, 23);
data.#name // Data 클래스의 name 변수는 # 프리픽스를 이용하여 은닉상태이기에 직접 접근은 불가능.
data.getData(); // &#39;name: siwoo, year: 22, season: spring&#39;
data.setName = &#39;shu&#39;;
data.getName; // &#39;shu&#39;</p>
<pre><code></code></pre></li>
</ul>
<blockquote>
<h3 id="참고">참고</h3>
</blockquote>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes/Private_class_fields">Private class fields[mdn]</a></li>
<li><a href="https://ui.toast.com/weekly-pick/ko_20200312">은닉을 향한 자바스크립트의 여정(ToastUI)</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[내가 몰랐던 자바스크립트]]></title>
            <link>https://velog.io/@dev_shu/%EB%82%B4%EA%B0%80-%EB%AA%B0%EB%9E%90%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
            <guid>https://velog.io/@dev_shu/%EB%82%B4%EA%B0%80-%EB%AA%B0%EB%9E%90%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</guid>
            <pubDate>Sat, 05 Mar 2022 13:45:52 GMT</pubDate>
            <description><![CDATA[<h3 id="1-레이블-구문">1. 레이블 구문</h3>
<ul>
<li>반복문이나 함수 앞에 레이블을 추가하면 break나 continue구문과 함께 사용 가능.</li>
<li>문법<pre><code class="language-js">label:
  statement</code></pre>
</li>
<li>break는 모든 레이블 구문에서 사용될 수 있으며, continue는 반복 레이블 구문에서만 사용 가능</li>
<li>예시
```js
loop1:
for (let i = 0; i &lt; 3; i++) {      //첫번째 for문은 &quot;loop1&quot; 레이블을 붙였다.
 loop2:
 for (let j = 0; j &lt; 3; j++) {   //두번째 for문은 &quot;loop2&quot; 레이블을 붙였다.<pre><code>if (i === 1) { // i가 1일 경우 2로 건너띔
   continue loop2;
}
console.log(&#39;i = &#39; + i + &#39;, j = &#39; + j);</code></pre> }
}
/* 
출력결과
i=0, j=0
i=0, j=1
i=0, j=2
i=2, j=0
i=2, j=1
i=2, j=2</li>
<li>/</li>
</ul>
<p>loop1:
for (let i = 0; i &lt; 3; i++) {      //첫번째 for문은 &quot;loop1&quot; 레이블을 붙였다.
   loop2:
   for (let j = 0; j &lt; 3; j++) {   //두번째 for문은 &quot;loop2&quot; 레이블을 붙였다.
      console.log(&#39;i = &#39; + i + &#39;, j = &#39; + j);
      if(i===1) continue; // continue loop2와 동일한 결과 출력
   }
}
// i,j를 다 출력
// 외부 루프문에서 숫자는 증가시지만 내부루프문은 변화가 없음</p>
<pre><code>- 간단한 블록에도 레이블을 사용할수 있음
```js
foo: {
    console.log(&#39;face&#39;);
      break foo;
    console.log(&#39;hehehe&#39;)
}
console.log(&#39;body&#39;);
/* 출력결과
&quot;face&quot;
&quot;body&quot;
*/</code></pre><ul>
<li>함수에도 레이블을 붙일 수 있지만, 엄격모드나 제너레이터 함수에는 붙일 수 없다.</li>
</ul>
<h3 id="2-objectfreeze">2. Object.freeze()</h3>
<ul>
<li>객체를 동결하는 메서드. 동결된 객체는 더 이상 값을 변경할수 없음</li>
<li>새로운 속성을 추가하거나 존재하는 속성을 제거하는 것을 방지</li>
<li>객체 안의 하나의 속성만 동결 가능</li>
<li>예시<pre><code class="language-js">const person = {
  name: &quot;siwoo&quot;,
    age:  25,
    favoriteFood: &quot;milk&quot;
}
console.log(person);
person.name = &quot;John&quot;;
console.log(person);
</code></pre>
</li>
</ul>
<p>Object.freeze(person);
/*
const person = Object.freeze({
    name: &quot;siwoo&quot;,
      age:  25,
      favoriteFood: &quot;milk&quot;
});
이런 식으로도 가능
*/
person.name = &#39;siwoo&#39;;
console.log(person.name); // &#39;John&#39; 출력
// 에러는 발생하지 않으나, 값이 변경되지 않고 그대로 출력됨
// 엄격모드에선 에러발생
// 에러내용: Error: Cannot assign to read only property &#39;name&#39; of object &#39;#<Object>&#39;</p>
<p>const person2 = Object.freeze({
    name: &quot;siwoo&quot;,
      age:  25,
      favoriteFood: &quot;milk&quot;,
      address: {
        street: &quot;1234&quot;
    }
});
// 추가적으로 중첩된 객체를 속성으로 추가하는 경우 변경가능
console.log(person2);
person2.address.street = &quot;5678&quot;;
console.log(person2);
// 중첩된 객체의 경우
// address: Object.freeze({street: &quot;1234&quot;})
// 동결 가능
// 객체의 속성이 값이 배열인경우에도 Object.freeze() 사용가능
// 이 경우에도 동결되어 있긴 때문에 배열의 변경 불가</p>
<pre><code>
### 3. Map

```js
/*
const CURRENCY_MAP = {
    &quot;United States&quot; : &quot;USD&quot;,
      &quot;India&quot;: &quot;Rupee&quot;
}
*/
/*
const CURRENCY_MAP = new Map([
    [&quot;Unite States&quot;, &quot;USD&quot;],
      [&quot;India&quot;, &quot;Rupee&quot;]
])
*/
const CURRENCY_MAP = new Map();
CURRENCY_MAP.set(&quot;United States&quot;, &quot;USD&quot;);
console.log(CURRENCY_MAP);

const india = {name: &quot;India&quot;};
CURRENCY_MAP.set(india, &quot;Rupee&quot;);
console.log(CURRENCY_MAP);</code></pre><h3 id="3-set">3. Set</h3>
<ul>
<li>고유한 값만 갖는 자료구조<pre><code class="language-js">const uniqueNumber = [1, 234, 234, 45];
</code></pre>
</li>
</ul>
<p>const set = new Set(uniqueNumber);</p>
<p>console.log(set) // [1,234,45]
/*
Set()의 메소드
has(), delete() =&gt; true,false 리턴
*/</p>
<pre><code>
### 4. Binary Math
```js
console.log(0.1+0.2); /0.30000000000000004
// 십진법의 수를 이진법으로 바꿔서 계산하고 다시 십진법으로 반올림하면서 결과가 저렇게 나옴.</code></pre><h3 id="5-console의-여러가지-함수들">5. console의 여러가지 함수들</h3>
<ul>
<li>time(), error(), assert(), table()<pre><code class="language-js">// time()
console.time(&quot;timer&quot;);
</code></pre>
</li>
</ul>
<p>for(let i=0; i &lt; 100000000; i++){
 //blabla 
}</p>
<p>console.timeEnd(&quot;timer&quot;);
// 이렇게 사용하면 for구문이 실행되는 시간을 구할수 있음
// 다만 사이사이에 주석달려있으면 그것도 포함해서 실행하기 때문에 실행시간 길어짐</p>
<p>// error()
const x = 2;
if(x !== 2) console.error(&quot;X is not 1&quot;);</p>
<p>// assert()
const x = 2;
console.assert(x === 1, &quot;X is not 1&quot;);</p>
<p>// table()
const people = [
  {name: &quot;Siwoo&quot;, age: 25},
  {name: &quot;Areum&quot;, age: 21},
  {name: &quot;Hyewon&quot;, age: 20}
]</p>
<p>// 테이블 형식으로 콘솔에 출력
console.table(people);</p>
<pre><code>
### 6. Debug
```js
const people = [
  {name: &quot;Siwoo&quot;, age: 25},
  {name: &quot;Areum&quot;, age: 21},
  {name: &quot;Hyewon&quot;, age: 20}
]
debugger
// 테이블 형식으로 콘솔에 출력
console.table(people);</code></pre><p><img src="https://images.velog.io/images/dev_shu/post/9d678fc3-5e38-4aeb-8678-c9cb42fe065c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-03-05%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.45.13.png" alt=""></p>
<p>! ### 참고</p>
<ul>
<li>Another 5 Must Know JavaScript Features That Almost Nobody Knows(<a href="https://youtu.be/yJDofSGTSPQ">https://youtu.be/yJDofSGTSPQ</a>)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트]]></title>
            <link>https://velog.io/@dev_shu/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
            <guid>https://velog.io/@dev_shu/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</guid>
            <pubDate>Sun, 13 Feb 2022 13:15:17 GMT</pubDate>
            <description><![CDATA[<h1 id="typescript">TypeScript</h1>
<ul>
<li>자바스크립트를 기반으로하는 프로그래밍언어</li>
<li>새로만들어진 어너아가 아니라 자바스크립트 언어를 사용하여
새로운 기능과 장점을 추가한 언어로 자바스크립트 코드를 더쉽고 강력하게 작성할수 있음</li>
<li>브라우저와 같은 자바스크립트 환경에서 실행할 수 없음. 이유는 브라우저는 타입스크립트를 실행할 수 없기 때문에.</li>
<li>타입스크립트 프로그래밍 언어면서 도구. 코드를 실행하여 타입스크립트 코드를 자바스크립트로 컴파일하는 강력한 컴파일러로 컴파일시 얻게 되는 것이 자바스크립트.</li>
<li>그렇다면 타입스크립트의 컴파일한 결과가 자바스크립트라면 어떻게 타입스크립트로 새로운 기능을 추가할 수 있을까?<ul>
<li>새로운 기능들을 자바스크립트 해결책으로 컴파일하는 타입스크립트 컴파일러</li>
</ul>
</li>
</ul>
<h3 id="비교">비교</h3>
<ul>
<li>자바스크립트<pre><code class="language-js">const button = document.querySelector(&quot;button&quot;);
const input_num1 = document.querySelector(&quot;#num1&quot;);
const input_num2 = document.querySelector(&quot;#num2&quot;);
</code></pre>
</li>
</ul>
<p>const add = (num1, num2) =&gt; {
  return num1+num2
}</p>
<p>button.addEventListener(&quot;click&quot;, () =&gt; {
  console.log(add(input_num1.value, input_num2.value))
});
// 만약 2,3을 전달했다면 결과는 5가 아닌 &#39;23&#39;
// 기술적인 문제가 아니라 논리적인 문제로 발생한 에러</p>
<pre><code>- 코드 보완
```js
const add = (num1, num2) =&gt; {
  if(typeof num1 === &quot;number&quot; &amp;&amp; typeof num2 === &quot;number&quot;) {
    return num1 + num2
  } else {
    return +num1 + +num2  
  }
}</code></pre><ul>
<li>타입스크립트의 경우<pre><code class="language-ts">const button = document.querySelector(&quot;button&quot;);
const input_num1 = document.querySelector(&quot;#num1&quot;)! as HTMLInputElement;
const input_num2 = document.querySelector(&quot;#num2&quot;)! as HTMLInputElement;
</code></pre>
</li>
</ul>
<p>const add: number = (num1:number, num2:number) =&gt; {
  return num1+num2
}</p>
<p>button.addEventListener(&quot;click&quot;, () =&gt; {
  console.log(add(+input_num1.value, +input_num2.value))
});</p>
<pre><code>- ! as HTMLInputElement라고 명시
- 또한 매개변수에 타입을 명시하여 코드 작성시 발생할수 있는 에러방지
- +기호를 통한 문자형 타입-&gt;숫자형타입으로 형변환

</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향이란?]]></title>
            <link>https://velog.io/@dev_shu/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@dev_shu/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Fri, 28 Jan 2022 14:40:16 GMT</pubDate>
            <description><![CDATA[<h3 id="1-객체지향이란">1. 객체지향이란?</h3>
<ul>
<li>시스템을 상호작용하는 <strong>자율적인 객체들의 공동체</strong>로 바라보고 객체를 이용해 시스템을 분할하는 방법.</li>
<li>자율적인 객체란 <strong>상태</strong>와 <strong>행위</strong>를 함께 지니며 스스로 자기 자신을 책임지는 객체를 의미</li>
<li>객체는 시스템의 행위를 구현하기 위해 다른 객체와 <strong>협력</strong>한다. 각 객체는 협력 내에서 정해진 역할을 수행하며 역할은 관련된 <strong>책임</strong>의 집합이다.</li>
<li>객체는 다른 객체와 협력하기 위해 메시지를 전송하고, <strong>메시지</strong>를 수신한 객체는 메시지를 처리하는 데 적합한 <strong>메서드</strong>를 자율적으로 수행한다.</li>
</ul>
<h3 id="2-객체지향의-목표">2. 객체지향의 목표</h3>
<ul>
<li>실세계를 모방하는 것이 아니라, 새로운 세계를 창조하는 것.</li>
<li>객체지향에서의 SW개발자의 역할은 단순히 실세계를 모방하는 것이 아니라 고객과 사용자를 만족시킬 수 있는 신세계를 창조하는 것</li>
<li>&quot;S/W시스템이 해결하려고 하는 실재는 잘해봐야 먼 친척밖에는 되지 않는다&quot; by 버트란드 마이어<ul>
<li>S/W 세계와 실세계 사이의 거리를 잘 표현함.</li>
</ul>
</li>
</ul>
<h3 id="3-실세계의-모방이라는-개념으로-oop를-설명하는-이유">3. 실세계의 모방이라는 개념으로 OOP를 설명하는 이유</h3>
<ul>
<li>실세계에 대한 비유가 객체지향의 다양한 측면을 이해하고 학습하는데 매우 효과적</li>
<li>객체를 스스로 생각하고 결정하는 현실 세계의 생명체에 비유하는 것은 상태와 행위를 <strong>&#39;캡슐화&#39;</strong>하는 SW 객체의 <strong>&#39;자율성&#39;</strong>을 설명하는 효과적.</li>
<li>현실 세계의 사람들이 암묵적인 약속과 명시적인 계약을 기반으로 협력하며 목표를 당성해 나가는 과정은 <strong>&#39;메시지&#39;</strong>를 주고 받으며 공동의 목표를 달성하기 위해 <strong>&#39;협력&#39;</strong>하는 객체들의 관계를 설명하는 적합함.</li>
<li>실세계의 사물을 기반으로 소프트웨어 객체를 식별하고 구현까지 이어간다는 개념은 객체지향 설계의 핵심사상인 <strong>&#39;연결완전성&#39;</strong>을 설명하는데 적합한 틀을 제공함.</li>
</ul>
<h3 id="4-커피주문으로-보는-객체의-협력-역할-책임">4. &#39;커피주문&#39;으로 보는 객체의 협력, 역할, 책임</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[내가 몰랐던 알아두면 좋은 태그들]]></title>
            <link>https://velog.io/@dev_shu/%EB%82%B4%EA%B0%80-%EB%AA%B0%EB%9E%90%EB%8D%98-%EC%95%8C%EC%95%84%EB%91%90%EB%A9%B4-%EC%A2%8B%EC%9D%80-%ED%83%9C%EA%B7%B8%EB%93%A4</link>
            <guid>https://velog.io/@dev_shu/%EB%82%B4%EA%B0%80-%EB%AA%B0%EB%9E%90%EB%8D%98-%EC%95%8C%EC%95%84%EB%91%90%EB%A9%B4-%EC%A2%8B%EC%9D%80-%ED%83%9C%EA%B7%B8%EB%93%A4</guid>
            <pubDate>Tue, 18 Jan 2022 11:43:36 GMT</pubDate>
            <description><![CDATA[<h3 id="progress-태그">progress 태그</h3>
<ul>
<li>로딩 관련 부분 태그
!codepen[siwoopak/embed/XWeQppb?default-tab=html%2Cresult]<h4 id="meter-tag">Meter tag</h4>
</li>
<li>progress 태그보다 좀 더 옵셔널하게 사용 가능
!codepen[siwoopak/embed/GRMLrdG?default-tab=html%2Cresult]<h3 id="date-tags">Date Tags</h3>
</li>
<li>input 태그의 타입의 값에 따라서 다르게 보여줌
!codepen[siwoopak/embed/eYGogEm?default-tab=html%2Cresult]</li>
</ul>
<h3 id="picture-tag">Picture Tag</h3>
<ul>
<li>해상도에 따라 다른 이미지를 보여주는 태그
!codepen[siwoopak/embed/zYEXNmK?default-tab=html%2Cresult]</li>
</ul>
<h3 id="datalist-tag">Datalist Tag</h3>
<ul>
<li>자동 검색 기능의 태그
!codepen[siwoopak/embed/BawEpMM?default-tab=html%2Cresult]</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[함수형 프로그래밍(4)]]></title>
            <link>https://velog.io/@dev_shu/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D4</link>
            <guid>https://velog.io/@dev_shu/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D4</guid>
            <pubDate>Mon, 10 Jan 2022 12:06:42 GMT</pubDate>
            <description><![CDATA[<h3 id="커링">커링</h3>
<blockquote>
<ul>
<li>함수에 인자를 하나식 적용해나가다가 필요한 지가 모두 채워지면 함수 본체를 실행하는 기법.</li>
</ul>
</blockquote>
<ul>
<li>다중 인수를 갖는 함수를 단일 함수를 갖는 함수들의 함수열로 바꾸는 것.</li>
<li>JS에서는 커링이 내장되어 있지 않지만, 일급함수가 지원되고 평가 시점을 다룰 수 있기 때문에 커링을 직접 구현할 수 있다.</li>
<li>커리 함수는 인자로 함수를 받고 커리 함수를 실행하는 즉시 함수를 리턴. 해당 함수가 실행되면 또 다른 함수가 연쇄적으로 실행됨.</li>
<li>커링 함수에서 앞에 있는 인자일수록 변동가성이 적게끔 구현하는 것이 좋음. 다라서 인자를 역으로 받아야 되는 경우를 고려하여 오른쪽에서부터 인자를 적용해 나가는 커링함수를 별도로 만들어 사용함.</li>
</ul>
<blockquote>
<p>참고</p>
</blockquote>
<ul>
<li><a href="https://1ilsang.dev/2020-02-17/js/currying">커링</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[함수형 프로그래밍(3)]]></title>
            <link>https://velog.io/@dev_shu/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D3</link>
            <guid>https://velog.io/@dev_shu/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D3</guid>
            <pubDate>Fri, 07 Jan 2022 13:20:29 GMT</pubDate>
            <description><![CDATA[<h3 id="다형성외부내부">다형성(외부/내부)</h3>
<pre><code class="language-js">console.log(
  [1,2,3,4].map(function(val){
    return val * 2;
  })
);
// 2,4,6,8 출력
console.log(
  [1,2,3,4].filter(function(val){
    return val % 2;
  })
);
// 1,3 출력</code></pre>
<p>다형성에 대해 얘기하기전에 기존에 만들었던 _map 함수와 _filter 함수는 이미 만들어져있다.</p>
<p>그래서 위와같이 사용이 가능하다.</p>
<p>명확히는 함수가 아닌 메서드로 만들어져있다.</p>
<p>무슨말이냐면 메서드는 어떤 객체, 여기서는 배열의 메서드라서 배열객체가 생성된 후에 그 배열객체에&#39;만&#39; 적용가능한 함수인 것이다.</p>
<pre><code class="language-js">console.log(
    document.querySelectorAll(&#39;*&#39;).map(function(node){
      return node.nodeName;
    });
);</code></pre>
<p>이렇게 찍어보면 어떨까?</p>
<p>document.querySelectorAll(&#39;*&#39;) 만 콘솔로 찍어보면 마치 배열처럼 [head, script, body, ... ] 이런식으로 나온다.</p>
<p>length도 보이고 배열의 인덱스도 보이지만 배열이 아니다.</p>
<p>이것은 array like 타입이기 때문에 위의 콘솔은 에러가 난다.</p>
<p>배열의 함수인 map을 불러서 그렇다.</p>
<p>이 문제를 어떻게 해결하느냐? 전에 만든 _map과 _filter는 함수다.</p>
<p><strong>즉, 어떤 객체에&#39;만&#39; 적용가능한 메서드가 아닌 함수라서 그 함수의 인자에 타입만 맞으면 모두 실행가능하다</strong>(외부 다형성)</p>
<p>이렇게 함수를 사용하면 외부적으로 다형성을 만들 수 있다는 얘기다. (_map, _filter, _each가 핵심 조건)</p>
<p>그럼 내부적으로 다형성은 누가 만들까?</p>
<p>바로 iter, mapper, predi 같은 인자로 넘어오는 함수들이다.</p>
<pre><code class="language-js">_map([1,2,3,4], function(v){
  return v + 10;
});</code></pre>
<p>그 배열에 어떤 값이든 수행할수 있게 만드는 역할은 보조함수가 하며, 그 인자로 들어오는 보조함수로 내부 다형성을 구현함.</p>
<p>개발자가 v가 숫자니까 더할꺼라든지 node가 노드니까 그 이름을 리턴한다든지 보조함수를 통해 정한다.</p>
<blockquote>
<p>참고 </p>
</blockquote>
<ul>
<li><a href="https://jeong-pro.tistory.com/55">기본기를 쌓는 정아마추어 코딩블로그</a></li>
<li><a href="https://www.inflearn.com/course/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/lecture/6778?tab=curriculum&amp;volume=1.00&amp;speed=1&amp;quality=auto">자바스크립트로 알아보는 함수형 프로그래밍(ES5) 다형성편</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[함수형 프로그래밍(2)]]></title>
            <link>https://velog.io/@dev_shu/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D2</link>
            <guid>https://velog.io/@dev_shu/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D2</guid>
            <pubDate>Wed, 05 Jan 2022 15:02:43 GMT</pubDate>
            <description><![CDATA[<h3 id="함수형으로-전환하기">함수형으로 전환하기</h3>
<h4 id="_map-_filter-_each">_map, _filter, _each</h4>
<pre><code class="language-js">const users = [
  { id: 1, name: &quot;ID&quot;, age: 36 },
  { id: 2, name: &quot;BJ&quot;, age: 32 },
  { id: 3, name: &quot;JM&quot;, age: 32 },
  { id: 4, name: &quot;PJ&quot;, age: 27 },
  { id: 5, name: &quot;HA&quot;, age: 25 },
  { id: 6, name: &quot;JE&quot;, age: 26 },
  { id: 7, name: &quot;JI&quot;, age: 31 },
  { id: 8, name: &quot;MP&quot;, age: 23 },
];

// 1. 명령형 코드
// 1. 30세 이상인 users를 거른다.
const temp_users = [];
users.forEach((user) =&gt; {
  if (user.age &gt;= 30) temp_users.push(user);
});
console.log(temp_users);
// 2. 30세 이상인 usersdml names를 수집한다.
const names = [];
temp_users.forEach((user) =&gt; {
  if (user.age &gt;= 30) names.push(user.name);
});
console.log(names);
// 3. 30세 미만인 users를 거른다.
const temp_users2 = [];
users.forEach((user) =&gt; {
  if (user.age &lt; 30) temp_users2.push(user);
});
console.log(temp_users2);
// 4. 30세 미만인 users의 ages를 수집한다.
const ages = [];
temp_users2.forEach((user) =&gt; {
  if (user.age &lt; 30) ages.push(user.age);
});
console.log(ages);
// 2. _filter, _map으로 리팩토링
// 1. _filter
const _filter = (lists, predi) =&gt; {
  const new_list = [];
  lists.forEach((el) =&gt; {
    if (predi(el)) new_list.push(el);
  });
  return new_list;
};

console.log(
  _filter(users, function (user) {
    return user.age &gt;= 30;
  })
);

console.log(
  _filter(users, function (user) {
    return user.age &lt; 30;
  })
);

console.log(
  _filter([1, 2, 3, 4], function (num) {
    return num % 2;
  })
);

console.log(
  _filter([1, 2, 3, 4], function (num) {
    return !(num % 2);
  })
);

// 2. _map
const _map = (lists, mapper) =&gt; {
  const new_list = [];
  lists.forEach((list) =&gt; {
    new_list.push(mapper(list));
  });
  return new_list;
};

const over_30 = _filter(users, function (user) {
  return user.age &gt;= 30;
});

const name_list = _map(over_30, function (user) {
  return user.name;
});

console.log(name_list);

const less_30 = _filter(users, function (user) {
  return user.age &lt; 30;
});

const age_list = _map(less_30, function (user) {
  return user.age;
});

console.log(&quot;&quot; + age_list);

// 3. 간단하게
const result1 = _map(
  _filter(users, function (user) {
    return user.age &gt;= 30;
  }),
  function (user) {
    return user.name;
  }
);
console.log(&quot;result1: &quot; + result1);
const result2 = _map(
  _filter(users, function (user) {
    return user.age &lt; 30;
  }),
  function (user) {
    return user.age;
  }
);
console.log(&quot;result2: &quot; + result2);</code></pre>
<h4 id="_each_map-_filter의-중복-코드-뽑아내기">_each(_map, _filter의 중복 코드 뽑아내기)</h4>
<pre><code class="language-js">const _each = (list, iter) =&gt; {
  list.forEach((el) =&gt; iter(el));
  return list;
};

const _map = (lists, mapper) =&gt; {
  const new_list = [];
  _each(lists, (val) =&gt; {
    new_list.push(mapper(val));
  });
  return new_list;
};

const result = _map(
  _filter(users, function (user) {
    return user.age &lt; 30;
  }),
  function (user) {
    return user.age;
  }
);
console.log(&quot;result: &quot; + result);</code></pre>
]]></description>
        </item>
    </channel>
</rss>