<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>anyway03 님의 블로그</title>
    <link>https://anyway03.tistory.com/</link>
    <description>anyway03 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Tue, 11 Aug 2026 06:32:37 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>anyway03</managingEditor>
    <item>
      <title>디스패처 서블릿이란?</title>
      <link>https://anyway03.tistory.com/11</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;디스패처 서블릿이란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;역할 :&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 라우팅 - 사용자의 url요청을 보고 어떤 컨트롤러의 어떤 메서드를 호출해야 하는지 찾아간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 바인딩 - http파라미터를 자바 객체로 변환하거나, json데이터를 객체로 매핑한다.(핸들러 어댑터)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰 렌더링 - 컨트롤러가 데이터(model)와 뷰 이름을 넘겨주면 디스패처서블릿이 뷰 리졸버를 호출해 렌더링해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중앙 집중식 관리 - 과거에는 모든 서블릿을 web.xml에 일일이 등록해야 했으나 이제는 디스패처 서블릿이 모든 요청을 받아 적절한 컨트롤러로 배분한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;= 프론트 컨트롤러 패턴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;구조적 위치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;톰캣 : 웹 서버이자 서블릿 컨테이너. 필터 단계를 관리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 컨테이너 : bean들이 위치. 인터셉터 단계.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;실행 순서&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터(서블릿)&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        System.out.println(&quot;&amp;gt;&amp;gt;&amp;gt; [1. Filter] 톰캣 단계: 요청이 들어옴&quot;);
        
        // 다음 필터나 서블릿(디스패처 서블릿)으로 전달
        chain.doFilter(request, response);
        
        System.out.println(&quot;&amp;gt;&amp;gt;&amp;gt; [5. Filter] 톰캣 단계: 응답이 나감&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터셉터(스프링)&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println(&quot;&amp;gt;&amp;gt;&amp;gt; [2. Interceptor] 스프링 단계: 컨트롤러 호출 직전&quot;);
        // handler 파라미터를 통해 어떤 컨트롤러 메서드가 호출되는지도 알 수 있음
        return true; // true여야 컨트롤러로 진행됨
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println(&quot;&amp;gt;&amp;gt;&amp;gt; [4. Interceptor] 스프링 단계: 뷰 렌더링까지 완료됨&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;사용자 요청&lt;/b&gt; &amp;rarr; 톰캣이 받음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Filter 실행&lt;/b&gt; (톰캣 단계)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DispatcherServlet 도착&lt;/b&gt; (스프링의 입구)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Interceptor 실행&lt;/b&gt; (스프링 단계 시작)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Controller 실행&lt;/b&gt; (개발자가 짠 코드)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Interceptor 종료&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DispatcherServlet이 응답 생성&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Filter 종료&lt;/b&gt; (톰캣 단계 마무리)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최종 응답&lt;/b&gt; &amp;rarr; 사용자&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 계층이 나뉘어 있기 때문에, 보안(Spring Security)이나 인코딩처럼 &lt;b&gt;애플리케이션 전반에 걸친 공통 작업은 필터&lt;/b&gt;에서, 로그인 체크나 권한 제어처럼 &lt;b&gt;스프링 기능이 필요한 작업은 인터셉터&lt;/b&gt;에서 처리하는 것이 정석&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;preHandle과&amp;nbsp;afterCompletion은 스프링 프레임워크의&amp;nbsp;&lt;b&gt;HandlerInterceptor&amp;nbsp;인터페이스에 정의되어 있는 추상 메서드&lt;/b&gt;들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HandlerInterceptor&amp;nbsp;인터페이스에는 총 3개의 메서드가 정의되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 메서드는 컨트롤러(핸들러)가 실행되는 시점을 기준으로 호출된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;preHandle()&lt;/b&gt;:&amp;nbsp;&lt;b&gt;컨트롤러 호출 전&lt;/b&gt;에 실행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반환 타입이&amp;nbsp;boolean&lt;/li&gt;
&lt;li&gt;true를 리턴하면 다음 단계(다음 인터셉터나 컨트롤러)로 진행하고,&amp;nbsp;false를 리턴하면 거기서 요청을 중단 (주로 로그인 체크 시 사용)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;postHandle()&lt;/b&gt;:&amp;nbsp;&lt;b&gt;컨트롤러 호출 후, 뷰(View)가 렌더링되기 전&lt;/b&gt;에 실행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨트롤러가 작업한 결과물(ModelAndView)을 가공할 수 있음.&lt;/li&gt;
&lt;li&gt;최근처럼 REST API(@RestController)를 주로 사용하는 환경에서는 자주 쓰이지는 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;afterCompletion()&lt;/b&gt;:&amp;nbsp;&lt;b&gt;모든 작업(뷰 렌더링 포함)이 끝난 후&lt;/b&gt;에 실행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청 처리 중에 사용한 리소스를 반환하거나, 실행 시간을 측정하여 로그를 남길 때 유용&lt;/li&gt;
&lt;li&gt;컨트롤러에서 예외가 발생하더라도 이 메서드는 반드시 호출됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>백엔드</category>
      <category>서블릿</category>
      <category>스프링MVC</category>
      <author>anyway03</author>
      <guid isPermaLink="true">https://anyway03.tistory.com/11</guid>
      <comments>https://anyway03.tistory.com/11#entry11comment</comments>
      <pubDate>Sat, 14 Feb 2026 11:16:15 +0900</pubDate>
    </item>
    <item>
      <title>4주차 스터디 내용</title>
      <link>https://anyway03.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예외처리 파고들기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ControllerAdvice와 RestControllerAdvice의 차이 :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@RestControllerAdvice는 @ResponseBody가 붙어 있어 응답을 Json으로 내려준다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그렇다면 orElseThrow 와 ResponseEntity... 중 어떤게 더 효율적일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;구분&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;return ResponseEntity.status(...).body(...)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;orElseThrow(() -&amp;gt; new BusinessException(...))&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;처리 위치&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Controller&lt;/b&gt; (응답을 직접 제어)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Service&lt;/b&gt; (로직에만 집중)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;코드 스타일&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;if-else 문이 많아져 코드가 길어짐&lt;/td&gt;
&lt;td&gt;한 줄로 처리되어 가독성이 좋음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;유지보수&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;에러 응답 형식이 바뀌면 모든 컨트롤러를 수정해야 함&lt;/td&gt;
&lt;td&gt;Advice 클래스 한 곳만 수정하면 전체 적용됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;주요 용도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;간단한 프로젝트, 혹은 컨트롤러에서 즉시 끝낼 때&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실무/대규모 프로젝트&lt;/b&gt;에서 공통 에러 처리 시&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;orElseThrow 방식의 장점 :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 서비스 레이어에서 예외만 알리면 됨. 간편.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 트랜잭션 - RuntimeException을 던지면 진행중이던 DB작업이 롤백된다. return을 보내면 반영될 위험이 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 가독성: if-else 간소화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적인 흐름 :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 서비스 레이어에서 문제 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 예외(Throw) BusinessException 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;흐름 중단:&lt;/span&gt; 예외가 발생하는 즉시 이후의 코드는 실행되지 않고, 호출 스택을 따라 예외가 위로 던져짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. @RestControllerAdvice가 장착된 GlobalExceptionHandler가 이 예외를 감지하고 낚아챈다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-------&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭(Generic)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다양한 데이터 타입을 보관해야 할 때&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--Object 타입으로 만들면 반환된 값을 매번 타입캐스팅해야 하므로 안정성 문제가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;---제네릭 클래스를 사용하여 인스턴스 생성시 타입을 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>anyway03</author>
      <guid isPermaLink="true">https://anyway03.tistory.com/10</guid>
      <comments>https://anyway03.tistory.com/10#entry10comment</comments>
      <pubDate>Fri, 23 Jan 2026 12:56:29 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 스터디 - nonlocal</title>
      <link>https://anyway03.tistory.com/9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩 함수 내에서만 유효하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역이 아닌 상위 스코프를 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;실제 변수 바인딩:&lt;/b&gt; nonlocal로 선언된 변수를 수정하면 실제 상위 함수의 변수 값이 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 이유&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;파이썬의 객체지향&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768010697902&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def make_counter():
    count = 0  # 상위 함수의 변수 (Enclosing variable)

    def counter():
        nonlocal count
        count += 1  # count 값을 변경하기 위해 nonlocal 선언 필수
        return count
    
    return counter

my_counter = make_counter()
print(my_counter())  # 1
print(my_counter())  # 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;count 변수는 make_counter 호출이 끝난 뒤에도 메모리에 남아 my_counter를 호출할 때마다 값이 갱신된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 클로저(Closure) 구현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 캡슐화 (Encapsulation)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 함수에서만 접근 가능하도록 보호&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI(Starlette) 내부의 nonlocal 활용 사례&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 &lt;b data-index-in-node=&quot;3&quot; data-path-to-node=&quot;4&quot;&gt;StreamingResponse&lt;/b&gt;(데이터를 끊어서 보낼 때)나 &lt;b data-index-in-node=&quot;37&quot; data-path-to-node=&quot;4&quot;&gt;Timeout&lt;/b&gt; 처리 로직에서 '응답이 이미 시작되었는지' 혹은 '연결이 끊겼는지'를 체크하는 플래그(Flag)용으로 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768010908187&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async def some_asgi_app(scope, receive, send):
    response_started = False  # 응답 시작 여부를 기록하는 상태 변수

    async def sender(message):
        nonlocal response_started  # 상위 함수의 변수를 수정하겠다고 선언
        
        if message[&quot;type&quot;] == &quot;http.response.start&quot;:
            response_started = True  # 상태 업데이트
            
        await send(message)

    # 실제 비동기 로직 수행 중 sender 호출...
    await sender({&quot;type&quot;: &quot;http.response.start&quot;, &quot;status&quot;: 200})
    
    if response_started:
        # 응답이 이미 시작된 경우의 후속 처리
        pass&lt;/code&gt;&lt;/pre&gt;</description>
      <category>스터디</category>
      <category>nonlocal</category>
      <category>개발스터디</category>
      <category>파이썬</category>
      <author>anyway03</author>
      <guid isPermaLink="true">https://anyway03.tistory.com/9</guid>
      <comments>https://anyway03.tistory.com/9#entry9comment</comments>
      <pubDate>Sat, 10 Jan 2026 11:08:47 +0900</pubDate>
    </item>
    <item>
      <title>DJANGO 프로젝트의 Entry Point 및 내부 소스 분석</title>
      <link>https://anyway03.tistory.com/8</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;manage.py&lt;/h4&gt;
&lt;pre id=&quot;code_1766722337917&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,2,0&quot;&gt;Shebang(쉬뱅) 라인:&lt;/b&gt; 이 파일을 실행할 때 어떤 프로그램(python)을 사용할지 시스템에 알려주는 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1766722361631&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from setproctitle import setproctitle&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 중인 프로세스의 이름을 변경하는 라이브러리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;진입&lt;/h4&gt;
&lt;pre id=&quot;code_1766722680727&quot; class=&quot;aspectj&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;@csrf_exempt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 검사(CSRF)를 이 함수에서만 제외&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1766722897825&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fs = FileSystemStorage(...)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 저장할 도구(저장소)를 준비&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;OpenCV(이미지 처리 라이브러리)&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 149px;&quot; border=&quot;1&quot; data-path-to-node=&quot;8&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 43px;&quot;&gt;
&lt;td style=&quot;height: 43px; width: 22.093%;&quot;&gt;&lt;b&gt;코드 내용&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 77.907%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 22.093%;&quot;&gt;&lt;span data-path-to-node=&quot;8,1,1,0&quot;&gt;cv2.imread / cvtColor&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 77.907%;&quot;&gt;&lt;span data-path-to-node=&quot;8,1,2,0&quot;&gt;이미지 파일을 읽어오고, 처리 속도를 높이기 위해 색상을 **그레이스케일(회색)**로 변환합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 43px;&quot;&gt;
&lt;td style=&quot;height: 43px; width: 22.093%;&quot;&gt;&lt;span data-path-to-node=&quot;8,2,1,0&quot;&gt;cv2.threshold(..., OTSU)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 77.907%;&quot;&gt;&lt;span data-path-to-node=&quot;8,2,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,2,0&quot;&gt;이진화:&lt;/b&gt; 아주 밝은 회색은 흰색으로, 어두운 회색은 검은색으로 딱 나누어 글자를 도드라지게 합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 22.093%;&quot;&gt;&lt;span data-path-to-node=&quot;8,3,1,0&quot;&gt;sobel / filter2D&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 77.907%;&quot;&gt;&lt;span data-path-to-node=&quot;8,3,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,3,2,0&quot;&gt;에지(테두리) 검출:&lt;/b&gt; 글자의 외곽선을 따서 글자 덩어리가 어디 있는지 찾기 쉽게 만듭니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 22.093%;&quot;&gt;&lt;span data-path-to-node=&quot;8,4,1,0&quot;&gt;cv2.findContours(...)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 77.907%;&quot;&gt;&lt;span data-path-to-node=&quot;8,4,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,4,2,0&quot;&gt;윤곽선 찾기:&lt;/b&gt; 이미지 안에서 연결된 덩어리(글자나 표의 칸)들의 좌표를 찾아냅니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;12&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;&lt;b&gt;코드 내용&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.6279%;&quot;&gt;&lt;b&gt;상세 설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;&lt;span data-path-to-node=&quot;12,1,1,0&quot;&gt;cv2.boundingRect(contour)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.6279%;&quot;&gt;&lt;span data-path-to-node=&quot;12,1,2,0&quot;&gt;찾아낸 윤곽선을 감싸는 **직사각형 박스 좌표(x, y, 가로, 세로)**를 계산합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;&lt;span data-path-to-node=&quot;12,2,1,0&quot;&gt;if w &amp;gt; img.shape[1] / 8...&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.6279%;&quot;&gt;&lt;span data-path-to-node=&quot;12,2,2,0&quot;&gt;너무 작은 점이나 노이즈는 무시하고, 일정 크기 이상의 &quot;글자 덩어리&quot;만 골라냅니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;&lt;span data-path-to-node=&quot;12,3,1,0&quot;&gt;cv2.resize(..., INTER_CUBIC)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.6279%;&quot;&gt;&lt;span data-path-to-node=&quot;12,3,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,3,2,0&quot;&gt;이미지 확대:&lt;/b&gt; 글자가 너무 작으면 잘 안 읽히기 때문에 2배로 키워주는 전처리를 합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;&lt;span data-path-to-node=&quot;12,4,1,0&quot;&gt;cv2.imwrite(...)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.6279%;&quot;&gt;&lt;span data-path-to-node=&quot;12,4,2,0&quot;&gt;잘라낸 박스 이미지를 파일로 저장합니다. (나중에 OCR 엔진에 보낼 용도)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>스터디</category>
      <author>anyway03</author>
      <guid isPermaLink="true">https://anyway03.tistory.com/8</guid>
      <comments>https://anyway03.tistory.com/8#entry8comment</comments>
      <pubDate>Fri, 26 Dec 2025 14:18:02 +0900</pubDate>
    </item>
    <item>
      <title>몽고DB 데이터 덤프</title>
      <link>https://anyway03.tistory.com/7</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;버전 5에서 7로 업그레이드한다는 가정 하에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5 -&amp;gt; 6 -&amp;gt; 7 로 가는 대신 데이터만 덤프하여 7버전으로 이관하기로 결정.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;준비물:&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;기존 몽고DB&lt;/li&gt;
&lt;li&gt;&amp;nbsp;다른 버전 몽고DB&lt;/li&gt;
&lt;li&gt;&amp;nbsp;mongodb database tools&lt;/li&gt;
&lt;li&gt;&amp;nbsp;mongosh&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mongodb database tools 를 공식 페이지에서 받는다. &lt;a href=&quot;https://www.mongodb.com/try/download/community&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.mongodb.com/try/download/community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1765961010667&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Try MongoDB Community Edition&quot; data-og-description=&quot;Try MongoDB Community Edition on premise non-relational database including the Community Server and Community Kubernetes Operator for your next big project!&quot; data-og-host=&quot;www.mongodb.com&quot; data-og-source-url=&quot;https://www.mongodb.com/try/download/community&quot; data-og-url=&quot;https://www.mongodb.com/try/download/community-edition&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buQTRk/hyZOz3gP4Q/48IJcetiBMwqc4Cv9rFJS0/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601&quot;&gt;&lt;a href=&quot;https://www.mongodb.com/try/download/community&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.mongodb.com/try/download/community&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buQTRk/hyZOz3gP4Q/48IJcetiBMwqc4Cv9rFJS0/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Try MongoDB Community Edition&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Try MongoDB Community Edition on premise non-relational database including the Community Server and Community Kubernetes Operator for your next big project!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.mongodb.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;/bin 에 다양한 툴이 있지만, 지금은 딱 두 개만 사용하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mZNqa/dJMcafkRexd/NGTP7yDNMSyo8KNu3oaj80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mZNqa/dJMcafkRexd/NGTP7yDNMSyo8KNu3oaj80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mZNqa/dJMcafkRexd/NGTP7yDNMSyo8KNu3oaj80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmZNqa%2FdJMcafkRexd%2FNGTP7yDNMSyo8KNu3oaj80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;231&quot; height=&quot;180&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어 - dump&lt;/p&gt;
&lt;pre id=&quot;code_1765961192125&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; ./mongodump --uri &quot;mongodb://localhost:&amp;lt;port&amp;gt;&quot; --out &amp;lt;데이터 덤프 경로&amp;gt;/mongodb_dump&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어 - restore&lt;/p&gt;
&lt;pre id=&quot;code_1765961450047&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./mongorestore --port &amp;lt;포트 지정&amp;gt; --dir ~/mongodb_dump/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵션 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.4419%;&quot;&gt;--port&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 87.4418%;&quot;&gt;복원 작업을 수행할 MongoDB 7.0 서버의 포트를 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.4419%;&quot;&gt;--dir &amp;lt;경로&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 87.4418%;&quot;&gt;&lt;b&gt;덤프된 데이터가 들어있는 최상위 폴더&lt;/b&gt;의 경로를 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.4419%;&quot;&gt;--uri &quot;...&quot;&lt;/td&gt;
&lt;td style=&quot;width: 87.4418%;&quot;&gt;(선택 사항) 만약 사용자 인증(Username/Password)을 사용한다면 --uri &quot;mongodb://user:pass@localhost:&amp;lt;port&amp;gt;&quot; 형식을 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우선 덤프한 데이터가 저장될 디렉터리를 만들어 준다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mongodb_dump 폴더&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;명령어 - dump를 실행하면 몽고디비의 데이터베이스들이 mongo_dump 폴더에 생긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;132&quot; data-origin-height=&quot;68&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diBpEC/dJMcaiu4GYK/tQvOV7S6Be8qtKpSBTIAj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diBpEC/dJMcaiu4GYK/tQvOV7S6Be8qtKpSBTIAj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diBpEC/dJMcaiu4GYK/tQvOV7S6Be8qtKpSBTIAj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiBpEC%2FdJMcaiu4GYK%2FtQvOV7S6Be8qtKpSBTIAj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;132&quot; height=&quot;68&quot; data-origin-width=&quot;132&quot; data-origin-height=&quot;68&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령어 - restore 를 실행하면 데이터들이 복원된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 확인&lt;/p&gt;
&lt;pre id=&quot;code_1765961743328&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 몽고셸 접속
./mongosh

# 데이터베이스 목록 확인
show dbs

# 특정 데이터베이스 선택 후 컬렉션 확인
use &amp;lt;데이터베이스_이름&amp;gt;
show collections

# 이관된 데이터 갯수 비교
db.&amp;lt;데이터베이스 이름&amp;gt;.find()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>피드백 &amp;amp; 복습</category>
      <category>MongoDB</category>
      <category>몽고디비</category>
      <category>몽고디비명령어</category>
      <category>몽고디비버전</category>
      <category>몽고디비업그레이드</category>
      <author>anyway03</author>
      <guid isPermaLink="true">https://anyway03.tistory.com/7</guid>
      <comments>https://anyway03.tistory.com/7#entry7comment</comments>
      <pubDate>Wed, 17 Dec 2025 17:56:06 +0900</pubDate>
    </item>
    <item>
      <title>크롤링 코드 수리하기</title>
      <link>https://anyway03.tistory.com/6</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 작성된 코드의 유지보수를 위해 처음으로 크롤링 코드를 접해봄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행하며 알게된 것들을 복기 목적으로 정리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spider, scrapy, scrapy-selenium?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;scrapy-selenium(미들웨어)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스크래피 요청(Request)을 처리할 때 셀레니움 웹드라이버(필자는 크롬 사용) 런치, url로드, 종료해줌&lt;/li&gt;
&lt;li&gt;셀레니움을 통해 로드된 HTML을 가져와서 Scrapy의 표준 응답객체와 유사한 SeleniumResponse 객체로 변환하여 스파이더에게 전달.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JavaScript 실행이 필요한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;동적(Dynamic) 웹 페이지&lt;/b&gt;도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;scrapy-selenium&lt;/b&gt;&amp;nbsp; 을 사용하면 Scrapy의 프레임워크 내에서 쉽게 처리&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;selenium을 별도로 사용할 경우&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스파이더 내에서 직접 webdriver.Chrome() 등으로 WebDriver를 &lt;b&gt;수동으로 초기화&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;데이터 추출 후에는 driver.quit() 또는 driver.close()를 사용하여 WebDriver를 &lt;b&gt;수동으로 종료&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 크롤링 작동 플로우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 초기화 및 페이지 준비&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762324972350&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;driver: webdriver.Chrome = response.request.meta['driver']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scrapy-Selenium 미들웨어를 통해 이미 준비된 &lt;b&gt;Selenium WebDriver 객체&lt;/b&gt;를 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. XPATH로 특정 메뉴 버튼을 찾아 클릭하여 페이지 이동&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;driver.page_source를 반복적으로 확인하여 &lt;b&gt;JavaScript 동작으로 인한 DOM 변경&lt;/b&gt;이 멈출 때까지 기다려야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 스크롤 영역 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762325086076&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;driver.execute_script('arguments[0].setAttribute(&quot;style&quot;, &quot;overflow: auto;height: 600px;...&quot;)', left_con)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 뷰포트 밖 요소 참조문제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Selenium은 화면에 보이지 않는 요소(뷰포트 밖)에 대한 상호작용(클릭)이 불안정하거나 불가능할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;요소를 화면에 노출&lt;/b&gt;시킨 후 클릭&lt;/p&gt;
&lt;pre id=&quot;code_1762325144180&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;driver.execute_script(&quot;arguments[0].scrollIntoView...&quot;, _expand_btn)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. CDATA 처리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;XML 출력 시 &lt;b&gt;데이터 내부에 &amp;lt;나 &amp;gt; 기호&lt;/b&gt;가 포함되어 XML 문법 오류를 일으킬 가능성이 있다면, 반드시 &amp;lt;![CDATA[...]]]&amp;gt; 섹션으로 감싸야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이 경우, XML 생성 라이브러리가 &lt;b&gt;자동으로 CDATA 선언을 인코딩하지 않도록&lt;/b&gt; 주의.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;수정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. xpath 등으로 페이지 구조 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;예시 :&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MEGZz/dJMcaksGeZt/FDab99Nw8WQmVSO91CA3g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MEGZz/dJMcaksGeZt/FDab99Nw8WQmVSO91CA3g1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MEGZz/dJMcaksGeZt/FDab99Nw8WQmVSO91CA3g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMEGZz%2FdJMcaksGeZt%2FFDab99Nw8WQmVSO91CA3g1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;685&quot; height=&quot;257&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;xpath -&amp;gt; /html/body/div[1]/section/div/div/div[2]/div/section/section[1]/h2&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;수집 로직&lt;/p&gt;
&lt;pre id=&quot;code_1762325595184&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        # 수집
        self.total_count += 1
        item = ArticleItem()
        self.custom_logger.info(f&quot;기사 '{book_id}' 수집 요청 - '{response.url}'&quot;)
        error_flag = False

        item[&quot;article_id&quot;] = article_id
        item['article_date'] = article_date

        try :
            article_selector_list = response.xpath(&quot;//div[@id='innerbody']/div[not(self::div[@id='a'])]&quot;)
            articles_list = []

            for article in article_selector_list:
                collected_article = {}
                try :
                    article_id = article.xpath(&quot;./@id&quot;).get()  # {아이디}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>피드백 &amp;amp; 복습</category>
      <author>anyway03</author>
      <guid isPermaLink="true">https://anyway03.tistory.com/6</guid>
      <comments>https://anyway03.tistory.com/6#entry6comment</comments>
      <pubDate>Wed, 5 Nov 2025 15:53:44 +0900</pubDate>
    </item>
    <item>
      <title>pyarmor + pyinstaller</title>
      <link>https://anyway03.tistory.com/5</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;난독화 한 코드를 설치하기 편하도록 컴파일할 필요가 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- pyinstaller는 크로스 컴파일러가 아니므로 윈도우에서 번들링하면 윈도우에서만 실행 가능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- rocky 리눅스 환경에서 진행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미니콘다 가상환경 실행 후 진행&lt;/p&gt;
&lt;pre id=&quot;code_1761190731701&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CONDA ACTIVATE MYENV&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이아머, 파이인스톨러를 설치한다.&lt;/p&gt;
&lt;pre id=&quot;code_1761190701885&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install pyarmor
pip install pyinstaller&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난독화 과정을 확인해 보기 위해 간단한 코드를 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1761190793991&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat &amp;gt; test_input.py
print('Hello World!')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;36&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmubYu/dJMb9LxehkK/kHQtI0qXICBYE0wxaDTSC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmubYu/dJMb9LxehkK/kHQtI0qXICBYE0wxaDTSC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmubYu/dJMb9LxehkK/kHQtI0qXICBYE0wxaDTSC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmubYu%2FdJMb9LxehkK%2FkHQtI0qXICBYE0wxaDTSC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;392&quot; height=&quot;36&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;36&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난독화&lt;/p&gt;
&lt;pre id=&quot;code_1761190917751&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pyarmor gen test_input.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dist/test_input에 생성된 파일 내용 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UaCk6/dJMb9VNisRC/oHuwyMaggAcKZg0enNrjF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UaCk6/dJMb9VNisRC/oHuwyMaggAcKZg0enNrjF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UaCk6/dJMb9VNisRC/oHuwyMaggAcKZg0enNrjF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUaCk6%2FdJMb9VNisRC%2FoHuwyMaggAcKZg0enNrjF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1354&quot; height=&quot;270&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(무서워)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난독화 완료.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 pyinstaller로 난독화 완료된 파일을 묶어 단일 실행파일을 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1761191121637&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pyinstaller -F dist/test_input.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;++ 만약 실행시 런타임 모듈이 필요하다는 경고가 뜨면, 난독화 할 때 생성된 런타임 폴더를 pyinstaller 대상과 같은 경로에 넣어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/77635741/pyarmor-modulenotfounderror-no-module-named-pyarmor-runtime-000000&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://stackoverflow.com/questions/77635741/pyarmor-modulenotfounderror-no-module-named-pyarmor-runtime-000000&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1761191165475&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Pyarmor ModuleNotFoundError: No module named 'pyarmor_runtime_000000'&quot; data-og-description=&quot;I'm trying to obfuscate my project but got error ModuleNotFoundError: No module named 'pyarmor_runtime_000000', So I tried to obfuscate a very simple folder containing only 2 files as follows test ...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/77635741/pyarmor-modulenotfounderror-no-module-named-pyarmor-runtime-000000&quot; data-og-url=&quot;https://stackoverflow.com/questions/77635741/pyarmor-modulenotfounderror-no-module-named-pyarmor-runtime-000000&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tCjwp/hyZLgWnO8J/ypHGk60WB1WWcSj8jFZ8H0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/77635741/pyarmor-modulenotfounderror-no-module-named-pyarmor-runtime-000000&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/77635741/pyarmor-modulenotfounderror-no-module-named-pyarmor-runtime-000000&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tCjwp/hyZLgWnO8J/ypHGk60WB1WWcSj8jFZ8H0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Pyarmor ModuleNotFoundError: No module named 'pyarmor_runtime_000000'&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I'm trying to obfuscate my project but got error ModuleNotFoundError: No module named 'pyarmor_runtime_000000', So I tried to obfuscate a very simple folder containing only 2 files as follows test ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+++ 의존성 문제가 발생할 수 있다. (no_module....)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어에 해당 문제가 발생한 의존성을 강제로 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 : uvicorn&lt;/p&gt;
&lt;pre id=&quot;code_1761283282295&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;pyinstaller --onefile --name my_app_release --collect-all uvicorn dist/난독화대상/main.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 --hidden-import 명령어를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1761283454736&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ModuleNotFoundError: No module named 'transformers.models.metaclip_2'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1761283479731&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;--hidden-import &quot;transformers.models.metaclip_2&quot;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>피드백 &amp;amp; 복습</category>
      <category>pyarmor</category>
      <category>Pyinstaller</category>
      <category>파이썬 난독화</category>
      <category>파이아머</category>
      <author>anyway03</author>
      <guid isPermaLink="true">https://anyway03.tistory.com/5</guid>
      <comments>https://anyway03.tistory.com/5#entry5comment</comments>
      <pubDate>Fri, 24 Oct 2025 14:24:59 +0900</pubDate>
    </item>
    <item>
      <title>Ollama vs vLLM</title>
      <link>https://anyway03.tistory.com/4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올라마와 vllm은 로컬에서 llm을 실행하기 위한 도구이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 모델을 지원하며 추론 방식을 가지고 있다. (vLLM의 PagedAttention, Ollama의 llama.cpp)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈소스 엔진으로 오픈소스 llm을 실행하면 나만의 챗지피티를 공짜로 사용할 수 있는 셈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 왜 다들 이렇게 사용하지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수많은 이유가 있겠지만, 직접 경험해본 사례와 빗대어 정리해 보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 높은 하드웨어 요구사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 LLM은 VRAM을 요구한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인보드에 장착되어 있는 RAM과 달리 연산/GPU 전문적인 메모리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 설치를 시도했던 GPU는 16GB의 VRAM을 가지고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. vLLM 으로 첫 시도, 실패&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vLLM 공식 도커 이미지를 사용해 설치를 시도해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 성능의 모델은 사용하기 힘든 환경이므로 qwen3 8b로 우선 진행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vLLM + 도커 실행의 장점 : 효율과 관리 용이 -&amp;gt; 선임이 알려주신 말씀이지만 구체적으로 경험해 보지 못했으므로 미래에 후술할 예정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단도직입적으로 말하자면 vllm으로 모델을 실행하는 시도는 실패했다. 메모리 부족이 원인이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Ollama 로 두번째 시도, 성공&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 올라마가 나설 차례였다.&amp;nbsp;사용법은 굉장히 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(우분투 기준)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모델 다운&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ollama pull 모델명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모델 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ollama run 모델명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;곧바로 cli환경에서 채팅이 시작된다.(대화 종료는 컨트롤 + D)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;oLLAMA는 메모리 관리를 기가 막히게 해준다. vllm으로 실행하지 못하던 모델도 어떻게든 올려주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 관리를 잘 해줄 뿐 근본적인 리소스가 늘어난 건 아니므로, 연산 도중 속도가 급격히 느려지는 구간이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상세한 로그 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ollama -v run 모델명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 로그 중 원인이 보이는 라인&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;msg=&quot;model weights&quot; device=CUDA0 size=&quot;11.8 GiB&quot;&lt;/li&gt;
&lt;li&gt;msg=&quot;model weights&quot; device=CPU size=&quot;1.1 GiB&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델의 일부가 VRAM 용량을 초과하면 cpu로 오프로드되며, 속도는 당연히 느려진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약 :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. ollama는 vllm를 사용해 실행 실패한 환경에서도 어떻게든 모델을 실행해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vLLM - VRAM 부족시 실패&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ollama - VRAM 부족시 cpu 폴백(느리긴 하나 실행은 됨)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. ollama는 vllm에 비해 확장성이 부족하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티유저 처리 힘듦&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 서버 성능이 충분하다면 vllm을 쓰자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그게 좋다는데 아직 이유는 모르겠음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신입 개발자의 얼렁뚱땅 복습...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>anyway03</author>
      <guid isPermaLink="true">https://anyway03.tistory.com/4</guid>
      <comments>https://anyway03.tistory.com/4#entry4comment</comments>
      <pubDate>Wed, 8 Oct 2025 23:52:55 +0900</pubDate>
    </item>
    <item>
      <title>miniconda로 가상환경 구축하기</title>
      <link>https://anyway03.tistory.com/3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 가상환경을 만들어두면 여러 프로젝트를 실행할 때 의존성 충돌 문제를 방지할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 프로젝트가 하나의 방을 사용할 수 있도록 해 주는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아나콘다나 venv 등을 사용하면 생성 방법도 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용한 것&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 3.11.9&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미니콘다 (파이썬 지원버전)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스(rocky9 사용)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- virtureBox 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 미니콘다 설치&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;wget 명령어로 온라인 저장소에서 쉽게 다운받을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1758802693237&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;wget https://repo.anaconda.com/miniconda/[파일이름]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 파이썬 버전과 호환되는 Miniconda 24.7.1-0 를 선택.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(만약 wget이 없으면 sudo dnf install wget으로 설치해주자.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-1. 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1758802815084&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bash Miniconda3-py39_24.7.1-0-Linux-x86_64.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 가상환경 설정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가상환경 확인 conda env list&lt;/li&gt;
&lt;li&gt;가상환경 활성화 conda activate &lt;b&gt;myenv(가상환경 이름)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;비활성화 conda deactivate&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 가상환경이 실행되면 파이썬 코드를 실행할 준비가 완료된 것&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp; .env 파일을 로드하려면 보통 python-dotenv가 필요. 인터넷이 가능한 환경이라면 pip명령어를 통해 쉽게 다운받을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1758802929251&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install fastapi uvicorn python-dotenv&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. API 서버 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 서버는 보통 Flask나 FastAPI를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Flask라면:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;export FLASK_APP=api.py&lt;/li&gt;
&lt;li&gt;# Windows에서는 set FLASK_APP=api.py flask run&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;FastAPI + uvicorn이라면:&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;uvicorn api:app --reload&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;( 위 명령어는 api.py 파일에 app = FastAPI() 식으로 app 인스턴스가 있어야 작동함)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;++ cli환경에서 db에 접속하는 방법도 간단.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;나는 postgresql을 사용하기 때문에 psql 명령어 사용.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1758803259096&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;psql -h [DB서버주소] -p [포트번호] -U [사용자이름] -d [데이터베이스명]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>피드백 &amp;amp; 복습</category>
      <category>록키</category>
      <category>리눅스</category>
      <category>미니콘다</category>
      <category>아나콘다</category>
      <category>파이썬</category>
      <author>anyway03</author>
      <guid isPermaLink="true">https://anyway03.tistory.com/3</guid>
      <comments>https://anyway03.tistory.com/3#entry3comment</comments>
      <pubDate>Thu, 25 Sep 2025 21:28:35 +0900</pubDate>
    </item>
    <item>
      <title>배포 연습 서버로 Ubuntu를 선택한 이유</title>
      <link>https://anyway03.tistory.com/2</link>
      <description>&lt;p data-end=&quot;654&quot; data-start=&quot;612&quot; data-ke-size=&quot;size18&quot;&gt;1. 서버 선택&lt;/p&gt;
&lt;p data-end=&quot;654&quot; data-start=&quot;612&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;654&quot; data-start=&quot;612&quot; data-ke-size=&quot;size16&quot;&gt;리눅스 서버용 OS를 고를 때 많이 언급되는 건 다음 두 가지다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;780&quot; data-start=&quot;656&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;726&quot; data-start=&quot;656&quot;&gt;&lt;b&gt;Rocky Linux&lt;/b&gt; : RedHat(RHEL) 계열. 안정성과 보안성이 강점. 대기업 서버 환경에 많이 사용된다.&lt;/li&gt;
&lt;li data-end=&quot;780&quot; data-start=&quot;727&quot;&gt;&lt;b&gt;Ubuntu&lt;/b&gt; : Debian 계열. 설치와 관리가 쉽고, 커뮤니티와 자료가 풍부하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;829&quot; data-start=&quot;782&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;829&quot; data-start=&quot;782&quot; data-ke-style=&quot;style2&quot;&gt;아래는 챗지피티가 비교해준 록키 리눅스 vs 우분투&lt;/blockquote&gt;
&lt;div&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1026&quot; data-start=&quot;831&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;항목&lt;/td&gt;
&lt;td&gt;Rocky Linux&lt;/td&gt;
&lt;td&gt;Ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;905&quot; data-start=&quot;878&quot;&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;887&quot; data-start=&quot;878&quot;&gt;설치 난이도&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;896&quot; data-start=&quot;887&quot;&gt;조금 복잡함&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;905&quot; data-start=&quot;896&quot;&gt;매우 쉬움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;936&quot; data-start=&quot;906&quot;&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;916&quot; data-start=&quot;906&quot;&gt;검색 자료 양&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;927&quot; data-start=&quot;916&quot;&gt;상대적으로 적음&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;936&quot; data-start=&quot;927&quot;&gt;매우 풍부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;982&quot; data-start=&quot;937&quot;&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;949&quot; data-start=&quot;937&quot;&gt;패키지 설치 방법&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;962&quot; data-start=&quot;949&quot;&gt;yum/dnf 사용&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;982&quot; data-start=&quot;962&quot;&gt;apt 사용 (명령어가 간단)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1026&quot; data-start=&quot;983&quot;&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;992&quot; data-start=&quot;983&quot;&gt;개발 친화성&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;1009&quot; data-start=&quot;992&quot;&gt;엔터프라이즈 서버에 최적화&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot; data-end=&quot;1026&quot; data-start=&quot;1009&quot;&gt;개발자 실습용으로 최적화&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽다는 설명도 매력적이지만, 실제 사용자도 우분투 쪽이 더 많다는 점이 설득력있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 연습의 목적이 &quot;리눅스 환경에 익숙해지는 것&quot;이기 때문에 초반에는 Ubuntu로 시작하고 나중에 필요하면 RHEL계열(Rocky 등)로 넘어가는 것이 훨씬 합리적이라고 판단.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. &lt;b&gt;Desktop(화면 있는 버전)&lt;/b&gt; 과 &lt;b&gt;Server(명령어 버전)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Ubuntu Server (CLI 버전)&lt;/b&gt; 을 설치하기로 결정. 리눅스 마스터 2급을 준비할 예정이기에 터미널 명령어에 익숙해질 필요가 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1883&quot; data-start=&quot;1851&quot;&gt;가상머신(VM)에 Ubuntu Server를 설치&lt;/li&gt;
&lt;li data-end=&quot;1960&quot; data-start=&quot;1884&quot;&gt;Spring Boot 애플리케이션을 빌드(mvn clean package)하고 .jar 파일을 리눅스 서버로 옮기기&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;물리적 하드는 공유하나 운영체제 파일 시스템은 독립적. [ NTFS(Windows) vs ext4(Linux) ]&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;가상머신은 완전히 별도 컴퓨터처럼 동작. 따라서 파일을 옮겨줘야 함.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2014&quot; data-start=&quot;1961&quot;&gt;리눅스 서버에서 Java를 설치, java -jar 명령어로 서버를 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+비전공자의 네트워크 이해에도 도움이 될 것 같다. (내&amp;nbsp;PC가 클라이언트, VM 안의 리눅스가 서버?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>백엔드</category>
      <category>jar</category>
      <category>ubuntu</category>
      <category>리눅스</category>
      <category>배포</category>
      <author>anyway03</author>
      <guid isPermaLink="true">https://anyway03.tistory.com/2</guid>
      <comments>https://anyway03.tistory.com/2#entry2comment</comments>
      <pubDate>Sun, 27 Apr 2025 15:46:36 +0900</pubDate>
    </item>
  </channel>
</rss>