<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Today I Learn</title>
        <link>https://velog.io/</link>
        <description>코더가 아니라 개발자가 되자</description>
        <lastBuildDate>Mon, 07 Nov 2022 00:32:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Today I Learn</title>
            <url>https://images.velog.io/images/sangeun-jo/profile/6c71b4f7-494d-4998-94b4-25e7546e9396/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Today I Learn. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sangeun-jo" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[우분투 특정 포트 죽이기 ]]></title>
            <link>https://velog.io/@sangeun-jo/%EC%9A%B0%EB%B6%84%ED%88%AC-%ED%8A%B9%EC%A0%95-%ED%8F%AC%ED%8A%B8-%EC%A3%BD%EC%9D%B4%EA%B8%B0</link>
            <guid>https://velog.io/@sangeun-jo/%EC%9A%B0%EB%B6%84%ED%88%AC-%ED%8A%B9%EC%A0%95-%ED%8F%AC%ED%8A%B8-%EC%A3%BD%EC%9D%B4%EA%B8%B0</guid>
            <pubDate>Mon, 07 Nov 2022 00:32:44 GMT</pubDate>
            <description><![CDATA[<p>8080포트 죽이는 법</p>
<pre><code>netstat -nap|grep 8080
sudo kill $(sudo lsof -t -i:8080)</code></pre><p><a href="https://coco-log.tistory.com/13">참고</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[암호화폐 거래소 시스템의 문제점]]></title>
            <link>https://velog.io/@sangeun-jo/%EC%95%94%ED%98%B8%ED%99%94%ED%8F%90-%EA%B1%B0%EB%9E%98%EC%86%8C-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90</link>
            <guid>https://velog.io/@sangeun-jo/%EC%95%94%ED%98%B8%ED%99%94%ED%8F%90-%EA%B1%B0%EB%9E%98%EC%86%8C-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90</guid>
            <pubDate>Tue, 09 Aug 2022 13:57:34 GMT</pubDate>
            <description><![CDATA[<p>암호화폐 거래소의 실시간 거래 트랜잭션 id(TXID = 트랜잭션 해시)를 가져오는 API가 필요한데 아무리 찾아도 없었다. </p>
<p>유명한 코인 거래소 몇 군데 고객센터에 문의를 해보니, 공통적으로 거래소에서 실시간으로 이루어지는 현물/선물 거래(spot/futhurs)는 트랜잭션 ID가 생성되지 않는다고 한다. 트랜잭션 아이디는 오직 deposit, withdrawal(거래소 지갑으로 입금, 거래소 지갑에서 외부지갑으로 송금, 외부 뱅크로 출금)할 때에만 생성된다고 한다. </p>
<p>트랜잭션 ID는 이전 트랜잭션의 해시와 연결되어 위조/변조 여부를 검증할 수 있게 해준다. 블록체인에 데이터가 추가될 때(transfer)에는 반드시 검증 과정을 거쳐야하고 해시와 연결되어야하는데, 코인 거래소의 실시간 거래에는 그 과정이 없다. 입출금을 제외한 나머지 거래는 코인 거래소 내부 데이터베이스의 숫자만 바뀌는 것이다. 모두와 함께 검증할 수 없고 언제든 조작될 수 있다. </p>
<p>거래소는 기존 은행/금융기관처럼 중앙화시스템이며 은행보다 보안도 허술하다. 기존 기술의 단점을 그대로 가져오면서 단점만 많아졌다. 블록체인 철학에도 위배된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[윈도우 Clojure 설치방법]]></title>
            <link>https://velog.io/@sangeun-jo/%EC%9C%88%EB%8F%84%EC%9A%B0-Clojure-%EC%84%A4%EC%B9%98%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@sangeun-jo/%EC%9C%88%EB%8F%84%EC%9A%B0-Clojure-%EC%84%A4%EC%B9%98%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 12 Jul 2022 13:53:44 GMT</pubDate>
            <description><![CDATA[<p>참고 <a href="https://applab.unc.edu/posts/2019/09/11/how-to-install-clojure-on-windows/">https://applab.unc.edu/posts/2019/09/11/how-to-install-clojure-on-windows/</a></p>
<p>자바는 설치되어 있다고 가정한다. </p>
<ol>
<li><p>아래 스크립트를 bat 파일로 저장
<a href="https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein.bat">https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein.bat</a></p>
</li>
<li><p>bat 파일이 있는 경로를 시스템 환경변수에 추가한다. </p>
</li>
<li><p>lein self-install 명령어 입력 </p>
</li>
<li><p>lein repl로 쉘 시작 </p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/sangeun-jo/post/d1ecdd68-d3d7-49cb-9d5f-61a0dda24158/image.png" alt=""></p>
<p>파이썬 이후로 오랜만에 정말 마음에 쏙 드는 언어를 찾았다. </p>
<p>파이썬은 자바와 같이 쓰일일이 거의 없어서 아쉬웠는데, Clojure는 자바랑 호환도 잘 된다니 정말 좋다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[access token으로 구글 자격증명 가져오기(feat. google drive api)]]></title>
            <link>https://velog.io/@sangeun-jo/access-token%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EA%B8%80-%EC%9E%90%EA%B2%A9%EC%A6%9D%EB%AA%85-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0feat.-google-drive-api</link>
            <guid>https://velog.io/@sangeun-jo/access-token%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EA%B8%80-%EC%9E%90%EA%B2%A9%EC%A6%9D%EB%AA%85-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0feat.-google-drive-api</guid>
            <pubDate>Thu, 23 Jun 2022 05:45:46 GMT</pubDate>
            <description><![CDATA[<p>oauth2 구글 로그인을 하고나서 얻어온 access token으로 google drive api를 사용하고 있다. GoogleCredenatial로도 잘 작동하지만, 앞으로 지원 종료될지도 모르기 때문에 며칠 삽질하며 코드를 업데이트 했다. 구글은 공식 문서 좀 보기 쉽게 업데이트 해줬음 좋겠다. 매번 찾아보기가 너무 힘들다...</p>
<h3 id="1-googlecredenatial-클레스에서-access-token으로-자격증명을-가져오는-방법더이상-사용되지-않음">1. GoogleCredenatial 클레스에서 access token으로 자격증명을 가져오는 방법(더이상 사용되지 않음)</h3>
<p>build.gradle</p>
<pre><code>implementation &quot;com.google.apis:google-api-services-drive:v3-rev197-1.25.0&quot;
implementation &#39;com.google.api-client:google-api-client:1.23.0&#39;//google apis
implementation &quot;com.google.http-client:google-http-client-jackson2:1.39.1&quot;</code></pre><pre><code>
public static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
public static final HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

   public Drive getDriveService(String accessToken) throws IOException {
        return new Drive.Builder(
                HTTP_TRANSPORT,
                JSON_FACTORY, getCredential(accessToken)
            )
            .setApplicationName(&quot;appname&quot;).build();
    }


    public Credential getCredential(String accessToken) {
        return new GoogleCredential.Builder()
                    .setClientSecrets(getClientSecrets())
                    .setJsonFactory(JSON_FACTORY)
                    .setTransport(HTTP_TRANSPORT)
                    .build()
                    .setAccessToken(accessToken);
    }

    private GoogleClientSecrets getClientSecrets() {
        GoogleClientSecrets clientSecrets = null; 
        try {
            InputStreamReader clientSecretsReader = new InputStreamReader(getSecretFile()); 
            clientSecrets = GoogleClientSecrets.load(JSON_FACTORY); 
        } catch (IOException e) {
            e.printStackTrace();
        }

        return clientSecrets;
    }

    private InputStream getSecretFile() throws IOException {
        return this.getClass().getResourceAsStream(&quot;/client_secret.json&quot;); //client scret 파일 경로(resources 폴더 기준 상대경로) 
    }
}</code></pre><h3 id="2-googlecredenatials-클레스에서-access-token으로-자격증명을-가져오는-방법">2. GoogleCredenatials 클레스에서 access token으로 자격증명을 가져오는 방법</h3>
<p>build.gradle</p>
<pre><code>implementation &#39;com.google.api-client:google-api-client:1.33.0&#39;
implementation &#39;com.google.apis:google-api-services-drive:v3-rev20211107-1.32.1&#39;
implementation &#39;com.google.http-client:google-http-client-gson:1.19.0&#39; 
implementation &#39;com.google.auth:google-auth-library-oauth2-http:1.3.0&#39; </code></pre><pre><code>public Drive getDriveService(String accessToken) throws IOException, GeneralSecurityException {
        return new Drive.Builder(
            GoogleNetHttpTransport.newTrustedTransport(),
            GsonFactory.getDefaultInstance(), 
                new HttpCredentialsAdapter(getCredentials(accessToken))
            )
               .setApplicationName(&quot;appname&quot;)
            .build();
}

 public GoogleCredentials getCredentials(String accessToken) throws FileNotFoundException, IOException {
        return GoogleCredentials.newBuilder()
            .setAccessToken(new AccessToken(accessToken, null)) 
            .build();
    }

/
* accessToken String 타입이 아니라 AcccessToken타입으로 얻어와서 설정해도 된다.  나는 다른곳에 쓰이는 로직 안고치려고 이렇게 했다. 
* accessToken 만료기한을 null 대신 Date 타입으로 줘도 된다. 그런데 임의로 설정한 만큼 유지가 되는지는 모르겠다. 
*/
</code></pre><p>훨씬 간단하고 깔끔해졌다!</p>
<p><a href="https://github.com/googleapis/google-api-java-client/pull/1440/files">참고코드</a></p>
<p>+) 서비스 계정으로 자격증명 생성하기 </p>
<pre><code>public GoogleCredentials getCredentials() throws FileNotFoundException, IOException {
        InputStream serviceSecret = getClass().getResourceAsStream(&quot;/service_secret.json&quot;);  //resources 기준 상대경로 
        GoogleCredentials credentials = GoogleCredentials.fromStream(serviceSecret); 
        credentials = credentials.createScoped(Arrays.asList(DriveScopes.DRIVE)); //scope이 없으면 자격 증명이 제대로 생성이 되지 않는다. 
        return credentials; 
} </code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[java.lang.NoSuchMethodError: com.google.api.client.http.HttpTransport.isMtls()Z]]></title>
            <link>https://velog.io/@sangeun-jo/java.lang.NoSuchMethodError-com.google.api.client.http.HttpTransport.isMtlsZ</link>
            <guid>https://velog.io/@sangeun-jo/java.lang.NoSuchMethodError-com.google.api.client.http.HttpTransport.isMtlsZ</guid>
            <pubDate>Tue, 21 Jun 2022 05:54:38 GMT</pubDate>
            <description><![CDATA[<p>처음에는 오래된 라이브러리를 써서 그런가 했는데 제일 최신버전으로 바꾸어도 똑같은 문제가 발생했다. 
의존성 충돌 문제인 것 같다. <code>./gradlew</code>를 터미널에서 실행하고 다시 서버를 시작하니 해결되었다. </p>
<p>_이것때문에 이틀을 날렸다. 
뭘 찾아보다가 해결한게 아니고 비주얼 스튜디오 코드에서 자동 빌드가 안되길래 수동으로 /gradlew 입력하여 빌드한 후 우연히 에러가 해결 되었다. 
잘 몰라도 포기하지 않으면 에러는 어떻게든 해결 되는 것 같다. _</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[google cloud sql 외부 접속 설정]]></title>
            <link>https://velog.io/@sangeun-jo/google-cloud-sql-%EC%99%B8%EB%B6%80-%EC%A0%91%EC%86%8D-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@sangeun-jo/google-cloud-sql-%EC%99%B8%EB%B6%80-%EC%A0%91%EC%86%8D-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Fri, 17 Jun 2022 00:19:59 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>방화벽 설정
VPC 네트워크 &gt; 방화벽 &gt; 방화벽 규칙 만들기
트레픽 방향: 수신
프로토콜 및 포트: tcp 3306</p>
</li>
<li><p>승인된 네트워크 설정 
<img src="https://velog.velcdn.com/images/sangeun-jo/post/899b4d8f-986c-46b6-a07f-ac953d055ac9/image.png" alt=""></p>
</li>
</ol>
<p>cmd에 ipconfig로 나오는 주소가 아니라, 구글에 my ip address 쳐서 나오는 주소를 입력해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[구글 시트 클론코딩 2일차]]></title>
            <link>https://velog.io/@sangeun-jo/%EA%B5%AC%EA%B8%80-%EC%8B%9C%ED%8A%B8-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-2%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@sangeun-jo/%EA%B5%AC%EA%B8%80-%EC%8B%9C%ED%8A%B8-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-2%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 03 Jun 2022 07:34:50 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/sangeun-jo/post/dd4a2d2b-6b2e-4537-81c7-ebbe10c27ed3/image.gif" alt=""></p>
<p>에디터블 테이블을 동적으로 생성하는 단계까지 완료했다. 
테이블 셀을 클릭하면 입력창이 활성화되고, 값을 바꿀 수 있다. 
1줄 추가 버튼을 누르면 테이블이 1줄 추가된다. 
앞으로 데이터베이스를 연동해 봐야겠다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[구글 시트 클론코딩 도전 ]]></title>
            <link>https://velog.io/@sangeun-jo/%EA%B5%AC%EA%B8%80-%EC%8B%9C%ED%8A%B8-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84</link>
            <guid>https://velog.io/@sangeun-jo/%EA%B5%AC%EA%B8%80-%EC%8B%9C%ED%8A%B8-%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EB%8F%84%EC%A0%84</guid>
            <pubDate>Wed, 01 Jun 2022 07:03:54 GMT</pubDate>
            <description><![CDATA[<p>완전히 똑같은 건 아니고 아래 기능만 구현하면 된다. </p>
<ol>
<li>editable 테이블 만들기</li>
<li>editable 테이블에 데이터베이스에서 가져온 데이터가 들어가도록 만들기 </li>
<li>editable 테이블 컬럼으로 필터하여 조회할 수 있게하기(구글 시트에서 필터 적용하면 컬럼에 뜨는 역삼각형 기호) </li>
<li>데이터 전체를 가져오는 것이 아니라, 트위터처럼 끝까지 내리면 재로딩 되는 무한 스크롤로 만들기(로딩되는 시간을 줄이기 위함)</li>
<li>구글 시트와 디자인 맞추기</li>
</ol>
<p>5번은 완료된거나 마찬가지다. 어떤 사람이 pug 와 scss 파일로 똑같이 만들어놓은게 이미 있기 때문이다.
<a href="https://codepen.io/oliviale/pen/rPjgmB">https://codepen.io/oliviale/pen/rPjgmB</a>
물론 pug scss에 대해 좀 공부를 하긴 해야한다. 나한테는 생소한 개념이다. </p>
<p>언어는 자바스크립트 + express.js로 할 것이다.
실패한다고 해도 많은 공부가 될 것 같다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ERROR 1698 (28000): Access denied for user 'root'@'localhost']]></title>
            <link>https://velog.io/@sangeun-jo/ERROR-1698-28000-Access-denied-for-user-rootlocalhost</link>
            <guid>https://velog.io/@sangeun-jo/ERROR-1698-28000-Access-denied-for-user-rootlocalhost</guid>
            <pubDate>Mon, 30 May 2022 03:18:50 GMT</pubDate>
            <description><![CDATA[<p>mariadb를 설치하고 처음 접속하면 sudo mysql로 비번없이 진입가능하다. 
sudo로 접속하여 아래 명령어로 비번을 설정하였다.</p>
<pre><code>use mysql;
update user set password=password(&#39;powerfull-password&#39;) where user=&#39;root&#39;;</code></pre><p>그러나 이상하게 계속 sudo로만 접속되고 mysql -u root -p 명령어로는 ERROR 1698 에러를 띄우며 접속이 되지 않았다. </p>
<p><a href="https://askubuntu.com/questions/1029177/error-1698-28000-access-denied-for-user-rootlocalhost-at-ubuntu-18-04">스택오버플로우</a>에서 해답을 찾았다. </p>
<p>sudo로 접속 후 다음 명령어를 입력하면 된다. </p>
<pre><code>USE mysql;
UPDATE user SET plugin=&#39;mysql_native_password&#39; WHERE User=&#39;root&#39;;
FLUSH PRIVILEGES;
exit;
service mysql restart</code></pre><p>이렇게 하고 나서는 sudo 로그인을 할 때에도 정확한 비번을 입력해야한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[mariadb root 비번 설정하기/변경하기]]></title>
            <link>https://velog.io/@sangeun-jo/mariadb-root-%EB%B9%84%EB%B2%88-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@sangeun-jo/mariadb-root-%EB%B9%84%EB%B2%88-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 14 May 2022 00:22:14 GMT</pubDate>
            <description><![CDATA[<p>처음 mariadb를 설치하였을 경우 root 비번 설정하는 법</p>
<pre><code>mysqladmin -u root password</code></pre><p>기존 설정한 비번을 바꾸고 싶을 경우</p>
<pre><code> mysqladmin -u root -p password</code></pre><p>패스워드 입력 후 새로운 패스워드를 설정할 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[오라클 ssh 접속 ]]></title>
            <link>https://velog.io/@sangeun-jo/%EC%98%A4%EB%9D%BC%ED%81%B4-ssh-%EC%A0%91%EC%86%8D</link>
            <guid>https://velog.io/@sangeun-jo/%EC%98%A4%EB%9D%BC%ED%81%B4-ssh-%EC%A0%91%EC%86%8D</guid>
            <pubDate>Fri, 13 May 2022 23:19:19 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>인스턴스를 생성할 때 ssh 키를 생성한다. 
기본적으로 linux8.5 운영체제가 선택되는 것 같다. </p>
</li>
<li><p>mobaxterm을 다운 받는다.
매번 이름을 까먹어서 &#39;푸티같은 프로그램&#39; 검색해서 찾고 있다..</p>
</li>
<li><p>mobaxterm 연결방법 
1) session &gt; ssh 클릭
2) remote host에 오라클 공개 ip 입력
3) advenced ssh setting 클릭하여 use private key 체크하고 비밀키파일 업로드
4) ok 누르고 username: 란에 opc 입력 </p>
</li>
</ol>
<p>ssh키는 분실하면 복잡해지므로 꼭 안전한 곳에 보관하자 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[flask + heroku 배포(하다가 만났던 에러들)]]></title>
            <link>https://velog.io/@sangeun-jo/flask-heroku-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@sangeun-jo/flask-heroku-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Thu, 21 Apr 2022 06:00:05 GMT</pubDate>
            <description><![CDATA[<h3 id="배포-준비">배포 준비</h3>
<ol>
<li><p>runtime.txt 작성 </p>
<pre><code>python-&#39;version&#39;
#ex python-3.10.4</code></pre></li>
<li><p>gunicorn 설치</p>
</li>
<li><p>requirements.txt 작성 </p>
</li>
</ol>
<pre><code>pip list --format=freeze &gt; requirements.txt</code></pre><p>format 옵션을 주지 않으면 버전이 아니라 경로가 포함되는 경우가 생기는데 이것때문에 에러가 발생할 수도 있다. 그래서 경로 없이 깨끗한 파일을 얻고 싶다면 꼭 저 옵션을 줘야한다. <em>전에는 이걸 몰라서 일일이 손으로 경로 지우고 버전 적는 노가다를 했었다..</em></p>
<ol start="4">
<li>Pro<strong>c</strong>file 작성 <pre><code>web: gunicorn &#39;메인 실행파일 이름(py붙이면 안됨)&#39;:app
#ex 메인실행파일이 main.py인 경우 web: gunicorn main:app</code></pre></li>
</ol>
<h3 id="heroku-배포-시-주의점">heroku 배포 시 주의점</h3>
<ol>
<li>heroku는 동적으로 port를 할당하기 때문에 꼭 지정된 포트에서 시작하지 못할 수 있다. 이 경우 지연되다가 아예 서버를 시작하지 못할 수도 있다고 한다. </li>
<li>host 옵션으로 0.0.0.0을 줘야한다.</li>
</ol>
<p>=&gt; 요약 app.run(host=&#39;0.0.0.0&#39;)</p>
<ol start="3">
<li>기타 앱, 데스크톱 혹은 TV용 구글 api 클라이언트 키를 사용할 경우 heroku에서 배포할 수 없다. 이거 몰라서 한 사흘 날림 </li>
</ol>
<h3 id="배포-중-만났던-에러들">배포 중 만났던 에러들</h3>
<ol>
<li><p><code>gunicorn: error: unrecognized arguments: app:app</code>
나는 flask 앱에 데스크톱 용 구글 api client 계정을 연동하여 사용하고 있었다.
데스크톱용 client 계정의 경우 브라우저가 자동으로 실행되고 사용자가 로그인을 하면 토큰을 받아오게 되는데 heroku는 실행할 수 있는 브라우저가 없었기 때문에 열리지 않았고, 응답을 하지 않은채로 계속 시간이 지나니 gunicorn에서 app을 찾을 수 없다는 에러를 낸 것이었다. <strong>서비스 계정으로 바꾸고나니 에러 없이 잘 배포되었다.</strong></p>
</li>
<li><p><code>H10 - App crashed</code> 
object-detection 의존성 문제
어차피 데이터 분석하거나 컴퓨터 비전쪽 웹서비스가 아니라서 그냥 모듈을 requirements.txt에서 지우니 해결 되었다. 충돌 일어나는 모듈 중에 꼭 필요한 모듈이 아니라면 그냥 지워도 된다. </p>
</li>
<li><p><code>code=H13 desc=&quot;Connection closed without response&quot;</code> 
페이지를 로드하지 못하고 30초 이상 지나면 에러로 표시된다. 
1번 문제를 해결하자 같이 고쳐졌지만 더 길게 설정할 수 있는 방법이 있다.  </p>
</li>
</ol>
<h3 id="후기">후기</h3>
<p>배포하는데 총 5일정도 걸렸다. 모듈 충돌 문제만 아니었으면 더 빨리 배포할 수 있었을 것이다. 꼭 가상환경 사용하자...의존성 문제 때문에 개발할 때 너무 힘들다. 
배포하는 과정에서 한 80%는 에러와 싸우는 과정인 것 같다. 다음에는 도커도 사용 해봐야겠다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[구글 api 서비스 계정으로 업로드한 구글 드라이브 파일 소유자 변경하기 ]]></title>
            <link>https://velog.io/@sangeun-jo/%EA%B5%AC%EA%B8%80-api-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B3%84%EC%A0%95%EC%9C%BC%EB%A1%9C-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%9C-%EA%B5%AC%EA%B8%80-%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B8%8C-%ED%8C%8C%EC%9D%BC-%EC%86%8C%EC%9C%A0%EC%9E%90-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@sangeun-jo/%EA%B5%AC%EA%B8%80-api-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B3%84%EC%A0%95%EC%9C%BC%EB%A1%9C-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%9C-%EA%B5%AC%EA%B8%80-%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B8%8C-%ED%8C%8C%EC%9D%BC-%EC%86%8C%EC%9C%A0%EC%9E%90-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 21 Apr 2022 04:23:54 GMT</pubDate>
            <description><![CDATA[<p>구글 api 서비스 계정으로 구글 드라이브에 업로드하면 서비스 계정이 파일의 소유자가 된다. </p>
<p>만약 에디터 권한을 가진 다른 사람이 서비스 계정으로 올린파일을 삭제하면 어디에서도 파일을 찾아볼 수 없다. 서비스계정으로는 구글 로그인을 할 수 없기 때문이다.</p>
<p>그래서 서비스 계정으로 파일을 올린다음 소유자를 변경하려고 시도했다.</p>
<p>그러나 에러의 에러의 에러를 반복하다가 결국 충분한 권한이 없다는 에러 메시지를 끝으로 포기했다.</p>
<p>그런데 콘솔에서 서비스 계정 설정을 편집자로, 소유자를 내 구글 계정을 설정하니, 업로드는 서비스계정으로 하는데 파일은 내가 소유하게 되었다. </p>
<p><img src="https://velog.velcdn.com/images/sangeun-jo/post/41028460-e628-4be8-ae9a-c8ed565c0843/image.png" alt=""></p>
<p>서비스계정 permissions 탭으로 들아간 후 Editor에 서비스 어카운트 계정을 추가하고 Owner에 파일 소유자로 하고 싶은 구글 계정을 추가한다. </p>
<p>설정하고 바로 적용이 안될 수도 있다. 적용 후 작동하지 않아서 안되는 줄 알았는데 며칠 후 다시 해보니 잘 되었다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[form안에 버튼이 여러 개일 때, 특정 버튼 눌렀을 때만 제출하게 하기   ]]></title>
            <link>https://velog.io/@sangeun-jo/form%EC%95%88%EC%97%90-%EB%B2%84%ED%8A%BC%EC%9D%B4-%EC%97%AC%EB%9F%AC-%EA%B0%9C%EC%9D%BC-%EB%95%8C-%ED%8A%B9%EC%A0%95-%EB%B2%84%ED%8A%BC-%EB%88%8C%EB%A0%80%EC%9D%84-%EB%95%8C%EB%A7%8C-%EC%A0%9C%EC%B6%9C%ED%95%98%EA%B2%8C-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@sangeun-jo/form%EC%95%88%EC%97%90-%EB%B2%84%ED%8A%BC%EC%9D%B4-%EC%97%AC%EB%9F%AC-%EA%B0%9C%EC%9D%BC-%EB%95%8C-%ED%8A%B9%EC%A0%95-%EB%B2%84%ED%8A%BC-%EB%88%8C%EB%A0%80%EC%9D%84-%EB%95%8C%EB%A7%8C-%EC%A0%9C%EC%B6%9C%ED%95%98%EA%B2%8C-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 17 Apr 2022 22:14:04 GMT</pubDate>
            <description><![CDATA[<h3 id="1-문제">1. 문제</h3>
<p>이런 폼이 있다고 해보자. 이미지를 업로드하고 크롭을 한 다음 제출해야한다. </p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;body&gt;
        &lt;form action=&quot;/req/submit&quot; method=&quot;POST&quot; &gt;
            ...
            &lt;button id=&quot;crop&quot;&gt;사진 크롭&lt;/button&gt;
            &lt;button id=&quot;submit&quot; type=&quot;submit&quot; &gt;&lt;/button&gt;&lt;/input&gt;
        &lt;/form&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre><p>그런데 사진 크롭 버튼을 누르면 그냥 제출되어버린다. 다음과같이 사진 크롭버튼을 밑으로 빼면? </p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;body&gt;
        &lt;form action=&quot;/req/submit&quot; method=&quot;POST&quot; &gt;
            ...
            &lt;button id=&quot;submit&quot; type=&quot;submit&quot; &gt;&lt;/button&gt;&lt;/input&gt;
        &lt;/form&gt;
        &lt;button id=&quot;crop&quot;&gt;사진 크롭&lt;/button&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre><p>제출 버튼 밑에 사진 크롭버튼이 있어서 안 예쁘다. </p>
<h3 id="2-해결-방안">2. 해결 방안</h3>
<p>실제 제출버튼을 안보이게 숨기고, 페이크버튼과 크롭버튼을 form 바깥으로 뺀다.<br>페이크 버튼을 누르면 실제 제출 버튼이 눌리게 하면 된다. </p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;body&gt;
        &lt;form action=&quot;/req/submit&quot; method=&quot;POST&quot; &gt;
            ...
            &lt;button style=&quot;display:none;&quot; id=&quot;submit&quot; type=&quot;submit&quot; &gt;진짜 제출 버튼&lt;/button&gt;
        &lt;/form&gt;
        &lt;button id=&quot;crop&quot;&gt;사진 크롭&lt;/button&gt;
        &lt;button onclick=&quot;submit();&quot;&gt;가짜 제출 버튼&lt;/button&gt;
        &lt;script&gt;
            function submit() {
                document.getElementById(&quot;submit&quot;).click();
            };
        &lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="3-더-간단한-해결-방안">3. 더 간단한 해결 방안</h3>
<p>button 태그(사진크롭)를 input 태그로 바꾸고 onclick이벤트를 할당한다.. </p>
<pre><code>&lt;input onclick=&quot;function()&quot;&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[localhost 외부공개하는 법 (test) ]]></title>
            <link>https://velog.io/@sangeun-jo/localhost-%EC%99%B8%EB%B6%80%EA%B3%B5%EA%B0%9C%ED%95%98%EB%8A%94-%EB%B2%95-test</link>
            <guid>https://velog.io/@sangeun-jo/localhost-%EC%99%B8%EB%B6%80%EA%B3%B5%EA%B0%9C%ED%95%98%EB%8A%94-%EB%B2%95-test</guid>
            <pubDate>Sun, 17 Apr 2022 19:04:16 GMT</pubDate>
            <description><![CDATA[<ol>
<li>글로벌하게 localtunnel 설치</li>
</ol>
<pre><code>npm install -g localtunnel</code></pre><ol start="2">
<li><p>localhost에서 프로젝트 돌리기 </p>
</li>
<li><p>lt --port &#39;포트번호&#39; </p>
</li>
</ol>
<p>정말 멋지다! 이제 테스트하려고 heroku에 배포하거나 라우터 포트포워딩할 필요없다!! </p>
<p>단점-오픈소스이기 때문에 종종 서버가 다운되는 일이 있다고 한다 .</p>
<p>그런게 싫으면 상업용 라이센스인 ngrok를 사용하면 된다. </p>
<p><a href="https://youtu.be/0lUJvVqSEkY">참고: Accessing Localhost From Anywhere In 5 Min!</a> </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[flask js, css 파일 html에서 가져오기]]></title>
            <link>https://velog.io/@sangeun-jo/flask-js-css-%ED%8C%8C%EC%9D%BC-html%EC%97%90%EC%84%9C-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0</link>
            <guid>https://velog.io/@sangeun-jo/flask-js-css-%ED%8C%8C%EC%9D%BC-html%EC%97%90%EC%84%9C-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0</guid>
            <pubDate>Sun, 17 Apr 2022 18:45:04 GMT</pubDate>
            <description><![CDATA[<p>우선 프로젝트 루트폴더에 static 폴더를 만들고 그 아래에 js, css 폴더를 만든다</p>
<p>폴더구조 </p>
<pre><code>project folder/
ㄴstatic/
    ㄴjs/
        ㄴjquery-1.11.3.min.js
        ㄴsignature_pad.min.js
    ㄴcss/
        ㄴcss.css
ㄴtemplates/</code></pre><p>templates 폴더의 html 파일에서 다음과 같이 사용한다. </p>
<pre><code>&lt;head&gt;
    &lt;script src=&quot;{{ url_for(&#39;static&#39;, filename=&#39;js/jquery-1.11.3.min.js&#39;)}}&quot; type=&quot;text/javascript&quot; /&gt;
    &lt;script src=&quot;{{ url_for(&#39;static&#39;, filename=&#39;js/signature_pad.min.js&#39;)}}&quot; type=&quot;text/javascript&quot; /&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;{{ url_for(&#39;static&#39;, filename=&#39;css/css.css&#39;)}}&quot;&gt;
&lt;/head&gt;
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[google drive api로 파일 업로드 후 permission 객체 리턴받기 ]]></title>
            <link>https://velog.io/@sangeun-jo/google-drive-api%EB%A1%9C-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%ED%9B%84-permission-%EA%B0%9D%EC%B2%B4-%EB%A6%AC%ED%84%B4%EB%B0%9B%EA%B8%B0</link>
            <guid>https://velog.io/@sangeun-jo/google-drive-api%EB%A1%9C-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%ED%9B%84-permission-%EA%B0%9D%EC%B2%B4-%EB%A6%AC%ED%84%B4%EB%B0%9B%EA%B8%B0</guid>
            <pubDate>Sat, 16 Apr 2022 21:04:23 GMT</pubDate>
            <description><![CDATA[<pre><code>file_resource = drive.files().create(body=metadata, media_body=path).execute()</code></pre><p>일반적으로 이렇게 호출하고 file_resorce를 받아보면 file id 같은 정보만 있고 permissions 객체가 없다.  </p>
<pre><code>file_resource = drive.files().create(body=metadata, media_body=path, fields=&#39;id, permissions&#39;).execute()</code></pre><p>이렇게 호출하면 permissions 객체를 리턴받을 수 있다. 
permissions 객체 중에서도 특정 필드값만 얻어오고 싶으면 다음과 같이 사용하면 된다 </p>
<pre><code>file_resource = drive.files().create(body=metadata, media_body=path, fields=&#39;id, permissions(id, role, emailAddress)&#39;).execute()</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[html 버튼 누르면 링크 이동]]></title>
            <link>https://velog.io/@sangeun-jo/html-%EB%B2%84%ED%8A%BC-%EB%88%84%EB%A5%B4%EB%A9%B4-%EB%A7%81%ED%81%AC-%EC%9D%B4%EB%8F%99</link>
            <guid>https://velog.io/@sangeun-jo/html-%EB%B2%84%ED%8A%BC-%EB%88%84%EB%A5%B4%EB%A9%B4-%EB%A7%81%ED%81%AC-%EC%9D%B4%EB%8F%99</guid>
            <pubDate>Thu, 14 Apr 2022 15:46:21 GMT</pubDate>
            <description><![CDATA[<p>link는 flask의 라우터에서 넘어온 변수이다. </p>
<pre><code>&lt;button onclick=&quot;location.href=&#39;{{link}}&#39;&quot;;&gt;see in google drive&lt;/button&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[google drive api로 google drive에 파일 업로드 ]]></title>
            <link>https://velog.io/@sangeun-jo/google-drive-api%EB%A1%9C-google-drive%EC%97%90-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C</link>
            <guid>https://velog.io/@sangeun-jo/google-drive-api%EB%A1%9C-google-drive%EC%97%90-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C</guid>
            <pubDate>Thu, 14 Apr 2022 04:14:25 GMT</pubDate>
            <description><![CDATA[<p>서비스 계정이 있고 키를 발급 받았다고 가정한다. </p>
<pre><code>metadata = {
        &#39;name&#39;: &#39;파일 이름.확장자&#39;, 
        &#39;parents&#39;: [&#39;업로드할 폴더 id&#39;], #필수아님
        &#39;mimeType&#39;: None
}

scope = [
    &#39;https://www.googleapis.com/auth/drive&#39;, 
]
json_file_name = &#39;./config/credentials.json&#39;
creds = ServiceAccountCredentials.from_json_keyfile_name(json_file_name, scope)
drive = build(&#39;drive&#39;, &#39;v3&#39;, credentials=creds)
file = os.path.join(os.getcwd(), &#39;temp&#39;, &#39;result.xlsx&#39;) #파일 경로 
res = drive.files().create(body=metadata, media_body=file).execute()
if res:
    print(&#39;Uploaded &quot;%s&quot; (%s)&#39; % (file, res[&#39;mimeType&#39;]))</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[google sheet column auto resize(auto fit) in colab]]></title>
            <link>https://velog.io/@sangeun-jo/google-sheet-column-auto-resizeauto-fit-in-colab</link>
            <guid>https://velog.io/@sangeun-jo/google-sheet-column-auto-resizeauto-fit-in-colab</guid>
            <pubDate>Thu, 14 Apr 2022 02:13:48 GMT</pubDate>
            <description><![CDATA[<pre><code>from google.colab import auth
auth.authenticate_user()
import gspread
from google.auth import default
creds, _ = default()
gc = gspread.authorize(creds)
wb = gc.open(&#39;file name&#39;)
s_hana = wb.worksheet(&#39;sheet name&#39;)
ws.columns_auto_resize(1, 8) #리사이즈 적용할 컬럼범위
ws = gc.create(filename).worksheet(&quot;Sheet1&quot;)</code></pre><p>만약 columns_auto_resize 함수를 찾을 수 없다고 하면 다음과 같이 gspread의 버전을 업그레이드 한다. </p>
<pre><code>!pip install gspread --upgrade
!pip install gspread_dataframe --upgrade</code></pre>]]></description>
        </item>
    </channel>
</rss>