<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jb-developer.log</title>
        <link>https://velog.io/</link>
        <description>개발자호소인</description>
        <lastBuildDate>Thu, 02 Nov 2023 07:52:20 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jb-developer.log</title>
            <url>https://velog.velcdn.com/images/jb-developer/profile/dd15ad87-16c8-4072-a236-0867e6ee956b/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jb-developer.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jb-developer" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[자바로 인스타그램 스크래핑하기(야매 초간단)]]></title>
            <link>https://velog.io/@jb-developer/%EC%9E%90%EB%B0%94%EB%A1%9C-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EC%8A%A4%ED%81%AC%EB%9E%98%ED%95%91%ED%95%98%EA%B8%B0%EC%95%BC%EB%A7%A4-%EC%B4%88%EA%B0%84%EB%8B%A8</link>
            <guid>https://velog.io/@jb-developer/%EC%9E%90%EB%B0%94%EB%A1%9C-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EC%8A%A4%ED%81%AC%EB%9E%98%ED%95%91%ED%95%98%EA%B8%B0%EC%95%BC%EB%A7%A4-%EC%B4%88%EA%B0%84%EB%8B%A8</guid>
            <pubDate>Thu, 02 Nov 2023 07:52:20 GMT</pubDate>
            <description><![CDATA[<h2 id="🔅개요🔅">🔅개요🔅</h2>
<p>딸깍으로 인스타 스크래핑하기</p>
<h3 id="코딩하기-전-주의사항">코딩하기 전 주의사항</h3>
<p>JSONObject와 JSONArray를 사용하고 있습니다.</p>
<p>지원하는 라이브러리 다운받아서 사용하시면 됩니다</p>
<h3 id="코드">코드</h3>
<pre><code>//마지막 username= 뒤의 부분을 원하는 인스타그램 주소로 접속하신 뒤
    //https://www.instagram.com/이부분/ 의 이부분을 복사해서 붙여넣기 하시면 됩니다
    static String apiUrl = &quot;https://www.instagram.com/api/v1/users/web_profile_info/?username=여기에복사&quot;;

    public static String getImageUrl(String apiUrl) throws Exception {

        //해당 주소의 첫번째 이미지 url을 불러온다(GET방식 - parameter:username)
        String urlPlain = &quot;&quot;;

        try{
            URL url = new URL(apiUrl);
            HttpURLConnection con = (HttpURLConnection)url.openConnection();
            con.setRequestMethod(&quot;GET&quot;);
            con.setRequestProperty(&quot;Viewport-Width&quot;, &quot;1020&quot;);

            //해당 부분 직접 채워주셔야합니다
            //하단에 이미지 넣어두었습니다
            con.setRequestProperty(&quot;X-Asbd-Id&quot;, &quot;&quot;);
            con.setRequestProperty(&quot;X-Csrftoken&quot;, &quot;&quot;);
            con.setRequestProperty(&quot;X-Ig-App-Id&quot;, &quot;&quot;);
            con.setRequestProperty(&quot;X-Ig-Www-Claim&quot;, &quot;&quot;);
            con.setRequestProperty(&quot;X-Requested-With&quot;, &quot;&quot;);

            int responseCode = con.getResponseCode();
            BufferedReader br;
            if (responseCode == 200) {
                br = new BufferedReader(new InputStreamReader(con.getInputStream()));
            } else {
                br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
            }
            String inputLine;
            StringBuffer response = new StringBuffer();
            while ((inputLine = br.readLine()) != null) {
                response.append(inputLine);
            }
            br.close();
            System.out.println(response.toString());
            JSONObject jsonob = new JSONObject(response.toString());

            jsonob = (JSONObject) jsonob.get(&quot;data&quot;);

            System.out.println(jsonob);

            jsonob = (JSONObject) jsonob.get(&quot;user&quot;);
            jsonob = (JSONObject) jsonob.get(&quot;edge_owner_to_timeline_media&quot;);
            JSONArray jsonarr = (JSONArray) jsonob.get(&quot;edges&quot;);
            jsonob = jsonarr.getJSONObject(0);
            jsonob = (JSONObject) jsonob.get(&quot;node&quot;);
            String a  = jsonob.get(&quot;thumbnail_src&quot;).toString();

            System.out.println(a);
            urlPlain = a;

        }catch(Exception e){
            e.printStackTrace();
        }

        return urlPlain;
    }

    public static void saveImage(String imageUrl) throws Exception {

        URL url = null;
        InputStream in = null;
        OutputStream out = null;

        try {
            url = new URL(imageUrl);
            in = url.openStream();

            // 컴퓨터 또는 서버의 저장할 경로(절대패스로 지정해 주세요.)
            //본인 새폴더 여시고 원하는 경로에 원하는 이미지 이름 붙여주시면 됩니다
            out = new FileOutputStream(&quot;C:\\Users\\Desktop\\새폴더\\예시용이미지이름.jpg&quot;);

            while (true) {
                // 루프를 돌면서 이미지데이터를 읽어들이게 됩니다.
                int data = in.read();

                // 데이터값이 -1이면 루프를 종료하고 나오게 됩니다.
                if (data == -1) {
                    break;
                }

                // 읽어들인 이미지 데이터값을 컴퓨터 또는 서버공간에 저장하게 됩니다.
                out.write(data);
            }

            // 저장이 끝난후 사용한 객체는 클로즈를 해줍니다.
            in.close();
            out.close();

        } catch (Exception e) {
            // 예외처리
            e.printStackTrace();
        } finally {
            // 만일 에러가 발생해서 클로즈가 안됐을 가능성이 있기에
            // NULL값을 체크후 클로즈 처리를 합니다.
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }


    }

    public static void main(String[] args) throws Exception {
        String imgUrl = getImageUrl(apiUrl);
        saveImage(imgUrl);
    }</code></pre><p>이미지 경로 첨부할 때</p>
<p>원하는 경로를 탐색기로 들어가셔서 위의 경로를 복사한 뒤</p>
<p>\을 추가하시고 뒤에 파일 이름을 적으시면 됩니다</p>
<h3 id="중간-리퀘스트-헤더-작성-부분-요령🔶">중간 리퀘스트 헤더 작성 부분 요령🔶</h3>
<p><img src="https://velog.velcdn.com/images/jb-developer/post/13db88d2-0b74-42da-82c5-a89f49aece50/image.png" alt=""></p>
<p>해당 주소 접속하셔서</p>
<p>개발자 도구를 여신 뒤(F12)</p>
<p>네트워크 클릭</p>
<p>밑의 Fetch/XHR 클릭하신 뒤</p>
<p>새로고침(Ctrl+R) 합니다</p>
<p>web_profile_info/?user<del>~</del> 부분 클릭</p>
<p><img src="https://velog.velcdn.com/images/jb-developer/post/fcdb3725-fa96-4acc-8dd4-42868a7064bb/image.png" alt=""></p>
<p>해당 빨간 부분에서 같은 글자 찾아서</p>
<p>값을 복사 붙여넣기 해주시면 됩니다</p>
<p>이후 메인메소드를 실행하면</p>
<p><img src="https://velog.velcdn.com/images/jb-developer/post/b7b3e4e2-e933-49c6-a347-6a34eaf62042/image.png" alt=""></p>
<p>원하는 폴더에 저장되고 종료합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEB server VS WAS]]></title>
            <link>https://velog.io/@jb-developer/WEB-server-VS-WAS</link>
            <guid>https://velog.io/@jb-developer/WEB-server-VS-WAS</guid>
            <pubDate>Sun, 17 Sep 2023 02:31:36 GMT</pubDate>
            <description><![CDATA[<h2 id="web은-뭐고-was는-뭐야❓">WEB은 뭐고 WAS는 뭐야❓</h2>
<p>대부분의 초보 개발자들이</p>
<p>WEB은 정적인 걸 전달하고 WAS는 동적인 걸 전달해준다?</p>
<p>이렇게 막연하게 대답하곤 한다.</p>
<p>업무 중에 서버 목록을 보면 </p>
<p>WEB도 여러 개 WAS도 여러 개 떠있는데</p>
<p>그럼 WEB이 저렇게 여러 대 떠있을 필요가 있는건가?</p>
<p>대체 WEB이 정확히 무슨 역할을 하는 것이고</p>
<p>WAS는 어디부터 어디까지 해주는건지 정확히 알고 싶어서 공부하기로 했다</p>
<h2 id="❓web-서버는-was안에-포함되어있다❓">❓WEB 서버는 WAS안에 포함되어있다❓</h2>
<p><img src="https://velog.velcdn.com/images/jb-developer/post/0203a13b-3823-4a20-b85a-a856518760fd/image.png" alt=""></p>
<blockquote>
<p>출처 : <a href="https://helloworld-88.tistory.com/71">https://helloworld-88.tistory.com/71</a></p>
</blockquote>
<p>WEB 서버는 WAS 안에 포함되어있다고 한다.</p>
<p>그럼 WEB과 WAS를 구분짓는건 저 Web container뿐인데</p>
<p>동적인 데이터 처리? 서블릿 구동 환경 제공?</p>
<p>알거같은데 모르겠음</p>
<h2 id="web-container의-역할">Web container의 역할</h2>
<p>JSP를 예로 들면 그 템플릿을 해석하는 역할이나(jsp의 반복문 등의 문법)</p>
<p>JAVA언어를 해석하여 페이지를 만들어내는 역할을 이 Web container가 수행한다고 한다</p>
<p>그럼 그렇게 다 만들어진 페이지는 최종적으로 정적으로 바뀌는 것이고</p>
<p>그걸 클라이언트한테 전달하는 역할을 웹 서버가 담당한다고 한다</p>
<p>완전히 정적인 페이지나, Web container의 해석으로 인해 만들어진 페이지를</p>
<p>단순히 전달만 해주는게 Web server의 역할인 것이다.</p>
<h2 id="⭐web-server가-하는-일은-그게-다일까">⭐web server가 하는 일은 그게 다일까?</h2>
<p>좀 세부적으로 들어가면 WEB server와 WAS가 하는 일은 더 많은데</p>
<p>우리가 http프로토콜로 요청과 응답을 주고 받는 것</p>
<p>정적인 리소스들의 캐싱</p>
<p>로드밸런싱이나 보안(SSL/TLS 암호화 등)의 역할을 WEB server가 맡고 있다고 한다.</p>
<p>내 생각에 쌩 기초에서 조금 벗어나는 내용이므로 나중에 다시 깊게 공부해야겠다.</p>
<h3 id="⭐was의-역할">⭐WAS의 역할</h3>
<p>WAS도 저런 템플릿 엔진, 언어 해석 외에도</p>
<p>세션관리나 데이터와의 연동 등 다양한 역할을 수행한다고 한다.</p>
<p>뭔가 코드 단에서 일어나는 일들은 전부 WAS가 담당한다고 생각해도 될것같다.</p>
<p>그래서 WEB보다 WAS서버가 더 많이 띄워져 있었던 것 같다</p>
<h2 id="⭐그럼-vue에서는-어떤-방식으로-돌아갈까">⭐그럼 Vue에서는 어떤 방식으로 돌아갈까</h2>
<p>흔히 우리가 아는 JSP처럼 요청이 들어오면 새로 그려주는 방식을</p>
<p><strong>서버 사이드 렌더링(SSR)</strong> 이라고 한다</p>
<p>WAS가 전부 만들어놓은 페이지를 요청에 따라 WEB server가 전달해주는 방식이다</p>
<p>하지만 Vue같은 클라이언트 사이드 렌더링 방식의 경우에는 그럼 어떻게 되는걸까?</p>
<p>그건 어느 쪽에서 일하는거지? 생각이 들어 알아보니</p>
<p>당연히 Client쪽에서 일하는것이였다...</p>
<p>데이터만 제공 받고 클라이언트 측에서</p>
<p>브라우져에 의해 계속 새로운 페이지를 렌더링하는것이다...</p>
<p>뭔가 허무하기도 하고</p>
<p>왜 CSR이 검색 엔진 노출에 불리한가 제대로 알게된것같다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[try-catch 리소스관리]]></title>
            <link>https://velog.io/@jb-developer/try-catch-%EC%86%8C%EC%8A%A4%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@jb-developer/try-catch-%EC%86%8C%EC%8A%A4%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Wed, 13 Sep 2023 12:33:03 GMT</pubDate>
            <description><![CDATA[<h2 id="🐔내가-대충-알던-try-catch🐔">🐔내가 대충 알던 try-catch🐔</h2>
<p>트라이 캐치문은 예외가 발생할 확률이 어느정도 있는 코드에서</p>
<p>예외상황을 처리하는데 사용되거나</p>
<p>혹은 일부러 예외상황을 발생시켜 커스텀한 예외처리로 이동시키고</p>
<p>하나의 로직처럼 사용하기도 한다</p>
<h3 id="근데-리소스-관리가-뭘까❓">근데 리소스 관리가 뭘까❓</h3>
<p>회사 자체적으로 돌리는 검출 프로그램에서</p>
<p>try-catch를 사용한 메소드들이 이슈리스트로 올라왔고</p>
<p>finally 블록에 할당된 리소스들을 해제하는 </p>
<p><strong>close()</strong> 메소드를 넣으라는 지시사항이 나왔다.</p>
<p>그동안 별 생각 없이 try 블록 안에서 해제했고</p>
<p>심지어 왜 해제해야 했는지도 정확히 모르고 </p>
<p>그냥 쓰라니까 쓰는가보다 습관적으로 넣고 있었는데...</p>
<p>갑자기 궁금해졌다</p>
<p>그리고 리소스 관리는 무슨 리소스를 말하는걸까</p>
<h3 id="close-메소드는-왜-필요할까">close() 메소드는 왜 필요할까</h3>
<p>리소스를 관리한다고 했을때</p>
<p>막연하게 메모리에 할당되는 자원이라고 생각하고 있었는데 아니였다</p>
<p>close() 메모리로 관리한다는 리소스는 운영체제의 자원이라고 한다</p>
<p>보통 close를 쓰는 경우는 파일전송을 위해 스트림을 연결할때, 커넥션을 만들 때였는데</p>
<p>파일을 옮길 경우 우리가 아는 기존 객체처럼</p>
<p><strong>메모리 자원 할당 -&gt; 가비지 콜렉터에 의한 할당 해제</strong></p>
<p>의 경우가 아니라 운영체제에 의해 쭈욱 연결되어있다고 한다</p>
<p>디비 커넥션도 마찬가지</p>
<blockquote>
<p><a href="https://okky.kr/questions/401102">https://okky.kr/questions/401102</a></p>
</blockquote>
<blockquote>
<p>우리가 프로그래밍 하고 있는것들은 memory를 제어하고 있는것입니다.
우리가 작업하고 있는 변수나 instance들은 memory에 있는 value와 address를 제어하는 것인데
File 은 hdd에 존재하는것 이므로 외부에 있는 자원이고 이 자원을 쓰려면 외부 자원을 open을 해서
메모리로 가지고 와야하며 다 사용하고 나면 다시 연결을 해제(close)해줘야 합니다.
마찬가지로 Connection도 외부 자원이고 쓰고나면 close를 해줘야합니다.</p>
</blockquote>
<p>서버를 내리기 전까지 계속 불필요한 연결이 이어진다는 것이고</p>
<p>이를 막기 위해서 코드로 끊어줘야 한다는 것이다.</p>
<p>추가로 close 메소드 실행 전에 예외가 발생할 경우엔</p>
<p>당연히 생겨난 스트림이 없어지지 않은 채로 catch 블록으로 이동할 것이고</p>
<p>close를 하지 않은것과 다름없어지게된다</p>
<p>그동안 어차피 예외가 발생해봤자 알아서 할당 해제되는줄 알고 크게 신경쓰지 않았는데...</p>
<h3 id="근데-flush는-그럼-뭘까">근데 flush는 그럼 뭘까</h3>
<blockquote>
<p> close는 스트림을 닫는 것, flush는 버퍼를 비우는 기능을 한다. 함께 써야 하는 이유는 스트림에서 에러가 발생해서 종료가 되더라도 버퍼에 데이터가 남아 있을 수 있기 때문이다. close의 경우, 내부적으로 flush를 호출하지만 꼭 flush를 해주는 것이 좋다고 한다.</p>
</blockquote>
<p>어렴풋이 기억에 남아있는 flush()</p>
<p>근데 회사코드에는 없었는데... 아마 붙여주는게 더 좋은 코드인것 같음</p>
<p>그리고 단순히 버퍼를 비워내는 것 뿐만 아니라 버퍼 안에 있는 것들을 전송시키면서 비워버린다 함</p>
<h3 id="그냥-try-with-resources를-쓰자😊😊">그냥 try with resources를 쓰자😊😊</h3>
<p>자바 7부터 사용할 수 있는 try-with-resources 구문은</p>
<p>트라이 블록을 열기 전에 미리 해제할 버퍼를 지정해주고</p>
<p>번거롭게 닫을 필요도 없도록 해준다.</p>
<p>또 finally문에서 버퍼를 사용했을때</p>
<p>먼저 객체를 null로 지정하고 바깥쪽 스코프에 돌도록 신경써야했던 귀찮음이 사라지고</p>
<p>코드도 더 가독성있고 이쁘게 사용할 수 있게 해준다</p>
<h4 id="기존-코드">기존 코드</h4>
<pre><code>FileInputStream input = null;

try{
    input = ......
    /////로직/////
}catch(예외){
    ///로깅///
}finally{
    if(input != null){
        try{
            input.close();
        }catch(){
            ........
        }
    }
}</code></pre><p>이 더러운 코드가</p>
<pre><code>try (FileInputStream input = new FileInputStream(&quot;파일 경로&quot;)) {
    ///// 로직 /////
} catch (예외) {
    /// 로깅 ///
}
</code></pre><p>이렇게 깔끔하게 끝이 난다...</p>
<p>깔끔하고 가독성 좋은 코드</p>
<p>➕ 상세한 주석 항상 생각하면서 코딩하자</p>
<p>➕➕➕ 참고로 try리소스구문은 AutoCloseable을 지원하는 객체에 한해서만 수거해준다고함</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue에서 ASYNC, AWAIT 써보기]]></title>
            <link>https://velog.io/@jb-developer/ASYNC-AWAIT-%EC%8D%A8%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@jb-developer/ASYNC-AWAIT-%EC%8D%A8%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Mon, 11 Sep 2023 10:01:03 GMT</pubDate>
            <description><![CDATA[<h2 id="vue의-라이프사이클-훅에서-async-사용">Vue의 라이프사이클 훅에서 async 사용</h2>
<p>Created에서 데이터를 받아온 뒤 mounted에서 화면에 띄워주려는데</p>
<p>데이터를 받아오는 속도에 따라 받아온 데이터가 화면에 적용되고 어떨때는 되지 않고</p>
<p>그런 문제점 때문에 이슈리스트에 해당 기능이 올라왔다</p>
<pre><code>
...

mounted() : {
    //데이터를 받아오는 함수(0.5초)
    ..............
    //데이터를 처리하는 함수(0.1초)
    ..............
},

...
</code></pre><p>mounted라는 훅 자체에 async를 사용할 수는 있지만 권장되지 않는 방법이라고 함</p>
<p>Vue의 라이프사이클 훅은 모두 동기적으로 동작하는데</p>
<p>mounted 자체에 async를 걸어봤자 그 안의 await처리된 함수들은 제대로 이행되더라도</p>
<p>다른 훅들이 기다려주지 않는다고 한다.</p>
<blockquote>
<p>Forcing Vue to delay creation or mounting or any of the other lifecycle methods to wait on your network request or long-running asynchronous process will impact the user in noticeable ways. Imagine a user coming to your site and then having to wait for 4 seconds with a blank screen while the component waits for the user&#39;s spotty cell connection to finish your network request.</p>
</blockquote>
<blockquote>
<p>(Vue를 강제로 생성하거나 마운트하거나 다른 라이프사이클 방법 중 하나를 네트워크 요청 시 대기하도록 하거나 오래 실행되는 비동기식 프로세스를 지연시키는 것은 사용자에게 눈에 띄는 방식으로 영향을 미칠 것입니다. 사용자가 사이트에 와서 구성 요소가 사용자의 얼룩진 셀 연결이 네트워크 요청을 완료하기를 기다리는 동안 빈 화면으로 4초 동안 기다려야 한다고 상상해 보십시오.)</p>
</blockquote>
<p>UX때문에 최대한 빈 화면을 오래 노출하지 않으려고 하는 것 같음</p>
<p>그래서 그냥 간단하게 method에 따로 빼놓고 async처리하면 됨</p>
<pre><code>
...

mounted() : {
    this.fetchData() //데이터를 받아오는 함수
},
methods : {
    async fetchData(){
        const data = await fetch(.......);

        //받아온 데이터를 이용할 로직들
    }
}

...
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Jquery 사용 시 XSS 방어법]]></title>
            <link>https://velog.io/@jb-developer/Jquery-%EC%82%AC%EC%9A%A9-%EC%8B%9C-XSS-%EB%B0%A9%EC%96%B4%EB%B2%95</link>
            <guid>https://velog.io/@jb-developer/Jquery-%EC%82%AC%EC%9A%A9-%EC%8B%9C-XSS-%EB%B0%A9%EC%96%B4%EB%B2%95</guid>
            <pubDate>Mon, 11 Sep 2023 09:41:20 GMT</pubDate>
            <description><![CDATA[<h2 id="append나-html사용에-주의">append나 html사용에 주의</h2>
<p>제이쿼리를 쓸 때 .append나 .html 을 사용하는 경우</p>
<p>스크립트를 집어넣는 XSS 공격에 취약해질 수 있다.</p>
<p>데이터를 불러와서 렌더링하는경우</p>
<p>스크립트 문구를 그냥 텍스트로 사용하여 막는것이 좋다.</p>
<pre><code>const data = 데이터를 받아와 리턴하는 함수



#안좋은 예
const insertHtml = &#39;&lt;input type=&quot;text&quot; .... value=&quot;&#39; + data.result.userName + &#39;&quot;&gt;&#39;
$(&quot;#userInfo&quot;).append(insertHtml)



#변경할 코드
const insertHtml = &#39;&lt;input type=&quot;text&quot; .... value=&quot;&quot;&gt;&#39;
$(&quot;#userInfo&quot;).append(insertHtml)
$(&quot;#userInfo&quot;).val(data.result.userName)
</code></pre><p>혹은 아예 유효성 검사를 진행한 후 사용해도 됨</p>
<pre><code>// 데이터를 받아올 때 유효성 검사를 수행
const userName = encodeHTML(data.result.userName);

// 유효성 검사를 한 값을 넣음
const insertHtml = `&lt;input type=&quot;text&quot; .... value=&quot;${userName}&quot;&gt;`;
$(&quot;#userInfo&quot;).append(insertHtml);

// 사용자 입력을 HTML 엔티티로 이스케이프하는 함수
function encodeHTML(input) {
  return input.replace(/&amp;/g, &quot;&amp;amp;&quot;).replace(/&lt;/g, &quot;&amp;lt;&quot;).replace(/&gt;/g, &quot;&amp;gt;&quot;).replace(/&quot;/g, &quot;&amp;quot;&quot;).replace(/&#39;/g, &quot;&amp;#39;&quot;);
}</code></pre><p>HTML 엔티티는 브라우저에서 스크립트에 사용되는 태그를 일반 문자로 인식하도록 해줌</p>
]]></description>
        </item>
    </channel>
</rss>