Callable, Futre는 Java 1.5에 추가된 Concurrency API 사양이다.
Callable은 Runnable과 주로 비교된다.
Callable vs Runnable
Runnable의 run() 메서드는 반환 값이 없으나 Callable의 call()은 완료시 결과를 리턴하게 되어 있다. 기존 Runnable의 run()은 반환 값이 없기 때문에 run() 메서드의 실행 결과를 구하기 위해서는 공용 메모리나 파이프 같은 것들이 필요하였다. Callable은 이를 개선하기 위해 마련된 인터페이스이다.
Callable
Callable 객체는 ExecutorService.submit() (submit 메서드는 Future 타입으로 리턴되고, execute 메서드는 void 타입이다.)과 같은 메서드에 던져진다. 던져진 작업은 큐에 보관되었다가 가용한 쓰레드가 생길 때 call() 메서드가 실행될 것이다. 쓰레드 풀에 던져진 작업은 바로 실행되는 것이 아니기 때문에 리턴 값은 미래의 어느 시점에 구할 수 있다.
Future
이러한 구조를 만들기 위해 Java에선 Future라는 인터페이스를 만들어 제공하고 있다. Future는 당장은 아니지만 언젠간 결과를 갖게되는 객체 정도로 여기면 될 것이다. Future는 기본적으로 Main 쓰레드와 분리된 쓰레드의 결과를 추적할 수 있는 한 가지 방법이 되는 것이다. Future의 get() 메서드는 Callable 인터페이스의 call() 메서드 실행이 완료될 때까지 블로킹 된다.
FutureTask
Future는 Runnable과 함께 작동하도록 만들 수 있는데 FutureTask라는 concrete 클래스가 이러한 역할을 해준다. FutureTask는 Runnable과 Future를 모두 구현하고 있다. FutureTask는 작업을 수행하는 쓰레드와 결과를 받는 쓰레드가 분리된 구조로 동작하고, 중간에 작업 완료 여부를 체크하거나 작업을 취소시킬 수도 있다.
참고: https://www.geeksforgeeks.org/future-and-futuretask-in-java/
2020년 2월 6일 목요일
[Java] ExecutorService, CountDownLatch
적절한 쓰레드 풀의 사용은 오래 걸리는 작업의 시간을 단축 시킨다.
아래와 같은 코드가 있고, doWork()이 완료되기까지 비교적 오랜 시간이 소요된다면.
위의 코드는 아래와 같은 형식으로 개선이 가능하다.
execute() 내부 동작이 병렬로 실행되므로 work 개체는 쓰레드 안정성을 확보하고 있어야 한다.
CountDownLatch가 전체 작업의 시작과 끝의 타이밍 제어를 해주고 있다.
참고: https://www.baeldung.com/java-countdown-latch
아래와 같은 코드가 있고, doWork()이 완료되기까지 비교적 오랜 시간이 소요된다면.
public void batchWork(Collection<Work> works) { for (Work work : works) { work.doWork(); } }
위의 코드는 아래와 같은 형식으로 개선이 가능하다.
execute() 내부 동작이 병렬로 실행되므로 work 개체는 쓰레드 안정성을 확보하고 있어야 한다.
public void batchWork(Collection<Work> works) { ExecutorService executor = Executor.newFixedThreadPool(4); CountDownLatch latch = new CountDownLatch(collection.size()); for (Work work : works) { executor.execute(() -> { work.doWork(); latch.countDown(); }) } latch.await(); }
CountDownLatch가 전체 작업의 시작과 끝의 타이밍 제어를 해주고 있다.
참고: https://www.baeldung.com/java-countdown-latch
HTML/CSS Margin 속성
마진은 태그 요소와 바깥 요소 간 간격(외부 여백, 패딩은 내부 여백.)을 지정한다. 1개부터 4개까지 값이 올 수 있다. 음수 값도 허용하는데, 음수 값을 적절히 이용하면 재미있는 배치를 표현할 수 있다.
margin을 지정하는 방법은 대략 아래와 같이 정리될 것이다. margin-top, margin-right 식으로 하나 하나 값을 따로 지정할 수도 있다.
1. 하나의 값
margin: 10px;
top, right, bottom, left 모두 10px.
2. 두개의 값 : 상하-우좌
margin: 10px 5px;
top, bottom: 10px
right, left: 5px
3. 세개의 값 : 상-우좌-하
margin: 10px 5px 15px;
top : 10px
right, left : 5px
bottom : 15px
4. 네개의 값 : 상-우-하-좌
margin: 10px 5px 15px 20px;
top 10px
right 5px
left 15px
bottom 20px
웹 환경에서만 개발하는 사람이라면 margin 값 지정에 별다른 어려움이 없겠지만 WPF 개발을 주로 하고 있어서 개인적으론 항상 헷갈린다.
(WPF에선 margin 적용이 left-top-righ-bottom 순이라 CSS의 top-right-bottom-left 순과 차이가 있다.)
margin을 지정하는 방법은 대략 아래와 같이 정리될 것이다. margin-top, margin-right 식으로 하나 하나 값을 따로 지정할 수도 있다.
1. 하나의 값
margin: 10px;
top, right, bottom, left 모두 10px.
2. 두개의 값 : 상하-우좌
margin: 10px 5px;
top, bottom: 10px
right, left: 5px
3. 세개의 값 : 상-우좌-하
margin: 10px 5px 15px;
top : 10px
right, left : 5px
bottom : 15px
4. 네개의 값 : 상-우-하-좌
margin: 10px 5px 15px 20px;
top 10px
right 5px
left 15px
bottom 20px
웹 환경에서만 개발하는 사람이라면 margin 값 지정에 별다른 어려움이 없겠지만 WPF 개발을 주로 하고 있어서 개인적으론 항상 헷갈린다.
(WPF에선 margin 적용이 left-top-righ-bottom 순이라 CSS의 top-right-bottom-left 순과 차이가 있다.)
2020년 2월 4일 화요일
HTML 주요 태그 정리.
• 문서를 구성하는 큰 골격에서의 주요 태그.
<doctype>
웹문서의 가장 위에 doctype 선언이 온다. doctype은 웹문서의 형식을 알려주기 위해 사용된다.
html5에선 doctype을 간단히 <!DOCTYPE html> 한가지로 정의한다.
<html>
doctype 다음에 html 태그가 오는데, 다른 모든 태그는 이 태그의 자손이 된다.
<head>
링크, 스크립트, 스타일시트의 정의 및 메타 태그가 들어간다.
<body>
실제 콘텐츠가 배치되는 곳, html 문서에서 본문에 해당한다.
<div>
<div> 자체론 아무 의미가 없지만 웹 페이지의 블럭을 지정하는 용도로 자주 쓰인다.
<script>
웹 문서에서 실행 가능한 스크립트를 넣거나 외부 스크립트를 참조할 때 쓴다. type 속성은 생략 가능하며 생략된 경우 JavaScript로 처리된다.
• 문서의 내용을 구성하는데 쓰이는 태그.
<article>
재배포, 재사용이 가능한 컨텐츠를 포함할 때 쓴다.
기사, 포럼 게시물, 사용자 코멘트 등이 대표적.
<br>, <hr>
<br>은 문서에서 줄바꿈이 필요할 때 사용된다.
<hr>은 문서 단락들 사이에 의미적인 분리를 위해 쓴다. 수평선으로 표시된다.
<canvas>
html5에 추가된 태그로 스크립트 언어를 이용해 그래픽을 표현할 수 있다.
<code>
프로그래밍 코드 조각을 표현할 때 사용된다.
<dl>, <dt>, <dd>
용어와 설명을 표시하는데 사용한다.
<embed>
플러그인 컨텐츠를 표시하는데 사용된다.
<figure>, <figcaption>
그림이나 도표의 제목 또는 범례를 표시하는데 사용된다.
<h1>, <h2>, <h3>, <h4>, <h5>, <h6>
문서의 제목에 사용된다.
<img>
사진을 표시할 때 사용된다. alt 속성으로 이미지가 표시되지 않을 때 대체 정보를 사용자에게 전달해줄 수 있다.
<li>, <ul>, <ol>
목록을 표시하는데 사용한다. 정렬 순서가 있는 경우 <li>를 <ol>로 감싸고, 정렬 순서가 없는 경우 <ul>로 감싸준다.
<p>
문서의 단락 정의에 사용된다.
<pre>
html 파일에 입력된 문자 그대로 보여줄 때 사용한다.
<section>
일반 문서 및 애플리케이션 영역을 표시한다. <article> 태그와 다르게 웹상에서 재배포를 할 수 없다는 의미를 담고 있다.
• <head>와 관련된 태그들.
<meta>
메타 정보를 정의한다.
<link>
CSS와 같은 외부 자원에 대한 링크를 정의한다.
<style>
CSS를 정의하는데 사용된다.
• 폼과 관련된 태그들
<form>
클라이언트와 서버 간 정보를 교환하기 위해 사용된다. method 속성으로 http 전송 방식을 지정해줄 수 있다.
<fieldset>
폼 양식에서 레이블과 컨트롤을 그룹으로 묶어 주기 위해 사용된다.
<input>
폼 양식에서 사용자 입력을 받을 때 사용한다. type으로 text, password, checkbox, radio, file, submit 등의 형식을 부여할 수 있다. (기본 값은 텍스트 박스.)
<label>
폼 양식에서 input 등 항목에 대한 설명을 붙일 때 사용한다.
<select>
폼 양식에서 <option> 태그와 함께 옵션을 선택하는 드롭다운 목록을 제공할 때 사용된다.
<textarea>
폼 양식에서 여러 줄로 구성된 텍스트를 입력받는 용도로 쓰인다. cols, rows 속성으로 표시되는 크기를 지정할 수 있다.
• 표와 관련된 태그들
<table>
데이터를 2차원 표로 표시할 때 사용된다.
<thead>
표에서 머리글 행을 표시할 때 사용된다.
<tbody>
표에서 내용에 대한 행을 표시할 때 사용된다.
<tr>
표에서 표의 행을 표시할 때 사용한다. <th> 또는 <td> 태그를 포함한다.
<td>
표의 셀. colspan, rowspan 등으로 셀 병합을 할 수 있다.
<th>
표에서 헤더, 제목이 되는 셀은 <th>로 표현된다.
<title>
문서의 제목을 정의한다. <head> 요소 내에 위치한다.
<caption>
표에서 제목을 정의할 때 사용된다.
<col>
표에서 열을 정의한다.
<colgroup>
표에서 열의 그룹을 정의할 떄 쓴다. <col> 태그를 포함한다.
• 블록 레벨 태그와 인라인 레벨 태그의 구분
# 블록 레벨 태그
<address>, <article>, <aside>, <blockquote>, <canvas>,
<dd>, <div>, <dl>, <dt>, <fieldset>, <figcaption>, <figure>,
<footer>, <form>, <h1>-<h6>, <header>, <hr>, <li>, <main>,
<nav>, <noscript>, <ol>, <p>, <pre>, <section>, <table>,
<tfoot>, <ul>, <video>
# 인라인 레벨 태그
<a>, <abbr>, <acronym>, <b>, <bdo>, <big>, <br>, <button>,
<cite>, <code>, <dfn>, <em>, <i>, <img>, <input>, <kbd>, <label>,
<map>, <object>, <output>, <q>, <samp>, <script>, <select>,
<small>, <span>, <strong>, <sub>, <sup>, <textarea>, <time>,
<tt>, <var>
<doctype>
웹문서의 가장 위에 doctype 선언이 온다. doctype은 웹문서의 형식을 알려주기 위해 사용된다.
html5에선 doctype을 간단히 <!DOCTYPE html> 한가지로 정의한다.
<html>
doctype 다음에 html 태그가 오는데, 다른 모든 태그는 이 태그의 자손이 된다.
<head>
링크, 스크립트, 스타일시트의 정의 및 메타 태그가 들어간다.
<body>
실제 콘텐츠가 배치되는 곳, html 문서에서 본문에 해당한다.
<div>
<div> 자체론 아무 의미가 없지만 웹 페이지의 블럭을 지정하는 용도로 자주 쓰인다.
<script>
웹 문서에서 실행 가능한 스크립트를 넣거나 외부 스크립트를 참조할 때 쓴다. type 속성은 생략 가능하며 생략된 경우 JavaScript로 처리된다.
• 문서의 내용을 구성하는데 쓰이는 태그.
<article>
재배포, 재사용이 가능한 컨텐츠를 포함할 때 쓴다.
기사, 포럼 게시물, 사용자 코멘트 등이 대표적.
<br>, <hr>
<br>은 문서에서 줄바꿈이 필요할 때 사용된다.
<hr>은 문서 단락들 사이에 의미적인 분리를 위해 쓴다. 수평선으로 표시된다.
<canvas>
html5에 추가된 태그로 스크립트 언어를 이용해 그래픽을 표현할 수 있다.
<code>
프로그래밍 코드 조각을 표현할 때 사용된다.
<dl>, <dt>, <dd>
용어와 설명을 표시하는데 사용한다.
<embed>
플러그인 컨텐츠를 표시하는데 사용된다.
<figure>, <figcaption>
그림이나 도표의 제목 또는 범례를 표시하는데 사용된다.
<h1>, <h2>, <h3>, <h4>, <h5>, <h6>
문서의 제목에 사용된다.
<img>
사진을 표시할 때 사용된다. alt 속성으로 이미지가 표시되지 않을 때 대체 정보를 사용자에게 전달해줄 수 있다.
<li>, <ul>, <ol>
목록을 표시하는데 사용한다. 정렬 순서가 있는 경우 <li>를 <ol>로 감싸고, 정렬 순서가 없는 경우 <ul>로 감싸준다.
<p>
문서의 단락 정의에 사용된다.
<pre>
html 파일에 입력된 문자 그대로 보여줄 때 사용한다.
<section>
일반 문서 및 애플리케이션 영역을 표시한다. <article> 태그와 다르게 웹상에서 재배포를 할 수 없다는 의미를 담고 있다.
• <head>와 관련된 태그들.
<meta>
메타 정보를 정의한다.
<link>
CSS와 같은 외부 자원에 대한 링크를 정의한다.
<style>
CSS를 정의하는데 사용된다.
• 폼과 관련된 태그들
<form>
클라이언트와 서버 간 정보를 교환하기 위해 사용된다. method 속성으로 http 전송 방식을 지정해줄 수 있다.
<fieldset>
폼 양식에서 레이블과 컨트롤을 그룹으로 묶어 주기 위해 사용된다.
<input>
폼 양식에서 사용자 입력을 받을 때 사용한다. type으로 text, password, checkbox, radio, file, submit 등의 형식을 부여할 수 있다. (기본 값은 텍스트 박스.)
<label>
폼 양식에서 input 등 항목에 대한 설명을 붙일 때 사용한다.
<select>
폼 양식에서 <option> 태그와 함께 옵션을 선택하는 드롭다운 목록을 제공할 때 사용된다.
<textarea>
폼 양식에서 여러 줄로 구성된 텍스트를 입력받는 용도로 쓰인다. cols, rows 속성으로 표시되는 크기를 지정할 수 있다.
• 표와 관련된 태그들
<table>
데이터를 2차원 표로 표시할 때 사용된다.
<thead>
표에서 머리글 행을 표시할 때 사용된다.
<tbody>
표에서 내용에 대한 행을 표시할 때 사용된다.
<tr>
표에서 표의 행을 표시할 때 사용한다. <th> 또는 <td> 태그를 포함한다.
<td>
표의 셀. colspan, rowspan 등으로 셀 병합을 할 수 있다.
<th>
표에서 헤더, 제목이 되는 셀은 <th>로 표현된다.
<title>
문서의 제목을 정의한다. <head> 요소 내에 위치한다.
<caption>
표에서 제목을 정의할 때 사용된다.
<col>
표에서 열을 정의한다.
<colgroup>
표에서 열의 그룹을 정의할 떄 쓴다. <col> 태그를 포함한다.
• 블록 레벨 태그와 인라인 레벨 태그의 구분
# 블록 레벨 태그
<address>, <article>, <aside>, <blockquote>, <canvas>,
<dd>, <div>, <dl>, <dt>, <fieldset>, <figcaption>, <figure>,
<footer>, <form>, <h1>-<h6>, <header>, <hr>, <li>, <main>,
<nav>, <noscript>, <ol>, <p>, <pre>, <section>, <table>,
<tfoot>, <ul>, <video>
# 인라인 레벨 태그
<a>, <abbr>, <acronym>, <b>, <bdo>, <big>, <br>, <button>,
<cite>, <code>, <dfn>, <em>, <i>, <img>, <input>, <kbd>, <label>,
<map>, <object>, <output>, <q>, <samp>, <script>, <select>,
<small>, <span>, <strong>, <sub>, <sup>, <textarea>, <time>,
<tt>, <var>
2020년 1월 8일 수요일
[Java] Java 소켓 통신 (NIO)
1. 요약
1.1 Java는 blocking 소켓과 non-blocking 소켓을 제공한다.
1.2 Blocking 소켓은 java.net 패키지에서 제공되고 쓰기 쉽다. 서버 입장에서 보면 멀티-쓰레딩 구조인데 동시 접속자가 적을 것으로 예상되는 소규모의 애플리케이션에선 이걸 써도 무방하다.
1.3 Non-blocking 버전은 java 1.4 부터 지원되었고, java.nio 패키지에서 제공된다. NIO(New I/O?)라고 부른다. Blocking 소켓에 비해 적은 비용으로 많은 클라이언트의 요청을 처리할 수 있다.
1.4 NIO의 ByteBuffer은 사용법이 좀 까다로워서 사용상 주의가 필요하다. Non-blocking 소켓을 써야만 하는 상황이라면 Netty framework이 더 나은 선택지일 수 있다. Netty는 조금 더 나은 성능에 더 쓰기 편한 'ByteBuf'를 제공한다.
2. Original Socket (Blocking)
(클라이언트) 소켓 인스턴스 생성은 아래와 같이 간단하다. 블로킹 방식으로 동작하기 때문에 클라이언트 입장에서 사용하기가 쉽다.
소켓 인스턴스가 서버에 연결되면 서버에 입력, 출력 스트림을 얻을 수 있다. 입력 스트림은 서버에서 데이터를 읽는 데 쓰이고, 출력 스트림은 서버에 데이터를 쓰는데 이용된다.
InputStream와 OutputStream은 일반 스트림이기 때문에 사용하기 편한 형식으로 변환해 사용할 수 있다.
InputStream와 OutputStream은 일반 스트림이기 때문에 사용하기 편한 형식으로 변환해 사용할 수 있다.
서버 측도 어렵진 않다. port를 선택해 listen 하고, accept()를 호출하면 클라이언트 연결이 될 때까지 블로킹 된다. 클라이언트가 서버에 연결되면 accept() 메서드는 서버가 클라이언트와 통신할 수 있는 소켓을 반환한다. 이는 클라이언트에 사용된 것과 같은 소켓 클래스이므로 마찬가지 방식으로 데이터를 읽고 쓸 수 있다.
서버는 클라이언트와 다르게 여러 클라이언트와 통신할 필요가 있으므로 accept()는 while 문 안에 놓고, 연결된 클라이언트가 있으면 이에 대한 핸들러는 별도의 쓰레드에서 동작시키는 구조로 로직을 작성하게 된다. 문제는 클라이언트가 많아질 때이다. 늘어나는 쓰레드는 서버에 부담을 준다. 물론 일반적인 규모의 프로그램에서는 굳이 non-blocking 소켓을 쓸 필요도 없고, 문제없이 동작할 것이다.
public class SocketServer extends Thread { private ServerSocket serverSocket; private int port; private volatile boolean running = false; public SocketServer(int port) { this.port = port; } public void startServer() { try { serverSocket = new ServerSocket(port); this.start(); } catch (IOException e) { e.printStackTrace(); } } public void stopServer() { running = false; this.interrupt(); } @Override public void run() { running = true; while (running) { try { System.out.println("Listening for a connection"); Socket socket = serverSocket.accept(); System.out.println("Client connected."); RequestHandler requestHandler = new RequestHandler(socket); requestHandler.start(); } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) { int port = 12345; SocketServer server = new SocketServer(port); server.startServer(); // Shutdown in 1 minute. try { Thread.sleep(60000); } catch(Exception e) { e.printStackTrace(); } server.stopServer(); } } class RequestHandler extends Thread { private Socket socket; RequestHandler(Socket socket) { this.socket = socket; } @Override public void run() { try { BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(socket.getOutputStream()); String line = in.readLine(); while(line != null && line.length() > 0) { out.println("Received: " + line); // Echo message out.flush(); line = in.readLine(); } in.close(); out.close(); socket.close(); } catch(Exception e) { e.printStackTrace(); } } }
2. New IO (NIO, non-blocking socket)
non-blocking 버전으로 Java 1.4 이후로 도입되었다. java.nio 패키지는 다음과 같은 주요 기능을 제공한다.
• 버퍼: 간단한 작업 집합으로 인터페이스 된 연속된 메모리 블록을 나타낸다.
• non-blocking I/O: 파일, 소켓과 같은 일반적인 I/O 소스에 '채널'을 연결해주는 역할을 한다.
Java에서 non-blocking 방식으로 통신하려면 우선 목적지(destination)에 대한 '채널'을 연 다음 '버퍼'를 이용해 서로 데이터를 주고 받아야 한다. 버퍼(java.nio.Buffer)엔 읽기/쓰기에 대한 위치(position), 버퍼의 고정 크기(capacity), 버퍼에 쓸 수 있는 데이터의 양(limit) 등의 속성이 있다. 주요 메서드론 clear(), flip(), rewind(), compact(), wrap() 등이 있는데 생각보다 까다로운 편. (Netty에선 쓰기 편하게 정리되어있다.)
NIO에선 AsynchronousServerSocketChannel이 ServerSocket의 역할을 대신한다. 채널을 열기 위한 open() 메서드가 있고, 특정 포트와 연결하기 위한 bind() 메서드가 있다. 그리고 클라이언트의 연결을 허용하는 accept() 메서드로 구성된다.
• 버퍼: 간단한 작업 집합으로 인터페이스 된 연속된 메모리 블록을 나타낸다.
• non-blocking I/O: 파일, 소켓과 같은 일반적인 I/O 소스에 '채널'을 연결해주는 역할을 한다.
Java에서 non-blocking 방식으로 통신하려면 우선 목적지(destination)에 대한 '채널'을 연 다음 '버퍼'를 이용해 서로 데이터를 주고 받아야 한다. 버퍼(java.nio.Buffer)엔 읽기/쓰기에 대한 위치(position), 버퍼의 고정 크기(capacity), 버퍼에 쓸 수 있는 데이터의 양(limit) 등의 속성이 있다. 주요 메서드론 clear(), flip(), rewind(), compact(), wrap() 등이 있는데 생각보다 까다로운 편. (Netty에선 쓰기 편하게 정리되어있다.)
NIO에선 AsynchronousServerSocketChannel이 ServerSocket의 역할을 대신한다. 채널을 열기 위한 open() 메서드가 있고, 특정 포트와 연결하기 위한 bind() 메서드가 있다. 그리고 클라이언트의 연결을 허용하는 accept() 메서드로 구성된다.
public class NioSocketServer { public NioSocketServer() { try { final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(12345)); listener.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() { @Override public void completed(AsynchronousSocketChannel ch, Void att) { listener.accept(null, this); // Accept the next connection. ch.write(ByteBuffer.wrap("Hello world\n".getBytes())); // Send hello message. ByteBuffer byteBuffer = ByteBuffer.allocate(4096); // Create read buffer. try { boolean running = true; int bytesRead = ch.read(byteBuffer).get(20, TimeUnit.SECONDS); // Read, timeout 20sec. while (bytesRead != -1 && running) { if (byteBuffer.position() > 2) { byteBuffer.flip(); // Ready to read. // Buffer data to string. byte[] lineBytes = new byte[bytesRead]; byteBuffer.get(lineBytes, 0, bytesRead); String line = new String(lineBytes); System.out.println("ECHO: " + line); ch.write(ByteBuffer.wrap(line.getBytes())); // ECHO byteBuffer.clear(); bytesRead = ch.read(byteBuffer).get(20, TimeUnit.SECONDS); } else { running = false; } } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { ch.write(ByteBuffer.wrap("Good Bye\n".getBytes())); // Send timeout message. } try { if (ch.isOpen()) { ch.close(); } } catch (IOException e1) { e1.printStackTrace(); } } @Override public void failed(Throwable exc, Void att) { // } }); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { new NioSocketServer(); System.out.println("Started."); try { Thread.sleep(60 * 1000); } catch(Exception e) { e.printStackTrace(); } System.out.println("Aborted."); } }
[Python] Python에서 언더스코어('_')가 갖는 의미.
Python을 쓰다보면 메서드나 변수의 앞과 뒤에 '_'나 '__'를 붙이는 경우를 자주 보게 된다. 값을 무시하는 용도로 쓰는 '_'에 대한 건 스킵하고, 이 글에선 이 두가지 경우에 대해서만 다루기로 한다..
1. Single Underscore
'_' 를 변수나 메서드 앞에 붙이는 건 단지 Python에서 쓰이는 이름 짓기 규칙일 뿐이다. 이는 특정 변수나 메서드가 내부 용임을 다른 프로그래머에게 알려주는 힌트로서 존재한다. 다만 '*' 임포트를 사용할 경우 모듈에서 정의된 메서드를 private 처럼 동작하게 해주는데, '*' 임포트 방식은 알다시피 권장되는 방식이 아니다.
'_' 가 변수의 끝에 붙는 경우는 변수의 이름 충돌을 피하기 위해 쓰이는 Python의 컨벤션이다.
2. Double Underscore, 던더
'__'와 같이 언더스코어 두 개를 변수나 메서드 앞에 붙이는 경우가 있다. 이는 Python 인터프리터 동작을 바꾸어주므로 단순 컨벤션이 아니다. 이러한 이중 언더스코어 prefix는 Python 인터프리터가 속성 이름을 다시 생성하게 해준다.(맹글링이라 한다.) 속성 이름 앞에 클래스 이름을 붙여주는 것인데 서브 클래스와의 이름 충돌을 피하게 해주어 서브 클래스에서 변수가 대체되지 않도록 보호해준다.
(private을 위한 장치라 생각할 수도 있긴 하지만 부수적인 효과일 뿐이고 실제 목적은 그러한 것이 아니다.)
마지막으로 '__'를 변수나 메서드 앞 뒤로 붙이는 경우가 있다. 이 경우엔 맹글링이 적용되지 않는다. 이는 특별한 문법적 기능이나 특별한 기능을 제공하기 위한 장치로 'magic' 또는 'special' 메서드라 불리운다. 대표적으로 '__init__' 이라든가 '__str__' 등이 있다.
1. Single Underscore
'_' 를 변수나 메서드 앞에 붙이는 건 단지 Python에서 쓰이는 이름 짓기 규칙일 뿐이다. 이는 특정 변수나 메서드가 내부 용임을 다른 프로그래머에게 알려주는 힌트로서 존재한다. 다만 '*' 임포트를 사용할 경우 모듈에서 정의된 메서드를 private 처럼 동작하게 해주는데, '*' 임포트 방식은 알다시피 권장되는 방식이 아니다.
# my_module.py def _internal_func(): return 42 >>> from my_module import * >>> _internal_func() NameError: "name '_internal_func' is not defined" >>> import my_module >>> my_module._internal_func() 42
'_' 가 변수의 끝에 붙는 경우는 변수의 이름 충돌을 피하기 위해 쓰이는 Python의 컨벤션이다.
2. Double Underscore, 던더
'__'와 같이 언더스코어 두 개를 변수나 메서드 앞에 붙이는 경우가 있다. 이는 Python 인터프리터 동작을 바꾸어주므로 단순 컨벤션이 아니다. 이러한 이중 언더스코어 prefix는 Python 인터프리터가 속성 이름을 다시 생성하게 해준다.(맹글링이라 한다.) 속성 이름 앞에 클래스 이름을 붙여주는 것인데 서브 클래스와의 이름 충돌을 피하게 해주어 서브 클래스에서 변수가 대체되지 않도록 보호해준다.
(private을 위한 장치라 생각할 수도 있긴 하지만 부수적인 효과일 뿐이고 실제 목적은 그러한 것이 아니다.)
마지막으로 '__'를 변수나 메서드 앞 뒤로 붙이는 경우가 있다. 이 경우엔 맹글링이 적용되지 않는다. 이는 특별한 문법적 기능이나 특별한 기능을 제공하기 위한 장치로 'magic' 또는 'special' 메서드라 불리운다. 대표적으로 '__init__' 이라든가 '__str__' 등이 있다.
2020년 1월 6일 월요일
[Java] 디렉터리 및 파일 삭제
아래의 코드는 사용자가 지정한 디렉터리 밑을 돌아다니면서 파일과 디렉터리를 삭제하는 코드다.
for 문 안에서 삭제하지 말아야 하는 패턴 같은걸 추가하면 더 안전하게 사용할 수 있다.
for 문 안에서 삭제하지 말아야 하는 패턴 같은걸 추가하면 더 안전하게 사용할 수 있다.
private void deleteFiles(String path) { File file = new File(path); if (file.exists() && file.isDirectory()) { File fileList[] = file.listFiles(); if (fileList == null) { return; } for (int i = 0, length = fileList.length; i < length; i++) { if (fileList[i].isDirectory()) { deleteFiles(fileList[i].getAbsolutePath()); fileList[i].delete(); // delete sub directory. } else if (fileList[i].isFile()) { fileList[i].delete(); } } file.delete(); // delete main directory. } }
피드 구독하기:
글 (Atom)