π‘ ν΄λΉ μ리μ¦λ μ°ν μ½μμ Tomcat ꡬννκΈ°μ λ―Έμ μ μ§ννλ©° 곡λΆν λ΄μ©μ κΈ°λ‘ν κΈμ λλ€.
π‘jdkλ azul-16μ μ¬μ©νμ΅λλ€.
π§ μλ‘
λ―Έμ μ μμμ Tomcatμ ν΅ν΄ κ°λ¨ν HTTP μλ²(μΉ μλ²)λ₯Ό ꡬννλ κ²μ λλ€.
Tomcatκ³Ό μΉ μλ²μ λν΄μ μ°Ύμ보면, WASμ μΉ μλ²μ μ°¨μ΄μ μ λν λ΄μ©μ΄ λ§μ΄ λ±μ₯ν©λλ€.
μ΄λ² κΈμμλ WASμ μΉ μλ²μ λν μ°¨μ΄μ μ λν΄μ μμ보λ κ²μ΄ λͺ©μ μ΄ μλλ―λ‘, λ€μκ³Ό κ°μ μ’μ μλ£λ€μ λν λ§ν¬λ₯Ό λ¨κ²¨ λκ³ , μ€λͺ μ 건λλ°λλ‘ νκ² μ΅λλ€.
- μμ½λ μμ(https://www.youtube.com/watch?v=Zimhvf2B7Es)
- μ¬μ°μ ν μ½ν‘(https://www.youtube.com/watch?v=31tKPguQ1sk)
- ν°κ±°μ ν μ½ν‘(https://www.youtube.com/watch?v=F_vBAbjj4Pk)
- μ리μ ν μ½ν‘(https://www.youtube.com/watch?v=mcnJcjbfjrs)
- ν¬λ΄μ ν μ½ν‘(https://www.youtube.com/watch?v=NyhbNtOq0Bc)
π§ μΉ μλ²κ° νλ μΌ
μΉ μλ²μ μν μΉ λΈλΌμ°μ μ κ°μ ν΄λΌμ΄μΈνΈλ‘λΆν° HTTP μμ²μ λ°μ μ μ μΈ μΉ νμ΄μ§λ₯Ό μλ΅ν΄μ£Όλ κ²μ λλ€.
μ΄λ μ¬μ©μμ μμ²μΌλ‘λΆν° μλ΅κΉμ§μ κ³Όμ μ λμ΄νλ©΄ λ€μκ³Ό κ°μ΅λλ€.
- ν΄λΌμ΄μΈνΈ 컀λ₯μ μλ½
- μμ² λ©μμ§ μμ
- μμ² μ²λ¦¬
- 리μμ€(HTML νμΌ λ±) λ§€νκ³Ό μ κ·Ό
- μλ΅ μμ±
- μλ΅ λ°ν
- μ°κ²° μ’ λ£
π‘ μμΌλ‘ μ΄ν΄λ³Ό μμΌλ‘ μμ±μμΌλκ° κΈ°λ³Έ λΌλ μ½λμ΄λ©°, νμ¬ μν©μμλ μΌλΆ μλμ½λ λλμΌλ‘ νννμμ΅λλ€.
π³ 0. κ°λ¨ν ν°μΊ£ ꡬν νμΈ
μμ²μ λ°λ₯Έ μ½λλ₯Ό μ΄ν΄λ³΄κΈ° μ μ, κ°λ¨ν ꡬνλ ν°μΊ£μ μ΄ν΄λ³΄λλ‘ νκ² μ΅λλ€.
λ¨Όμ λ©μΈ λ©μλμ λλ€.
public class Application {
public static void main(String[] args) {
Tomcat tomcat = new Tomcat();
tomcat.start();
}
}
Tomcat κ°μ²΄λ₯Ό μμ±νκ³ μμν©λλ€.
Tomcat ν΄λμ€λ λ€μκ³Ό κ°μ΄ ꡬνλμ΄ μμ΅λλ€.
public class Tomcat {
public void start() {
Connector connector = new Connector();
connector.start();
try {
// make the application wait until we press any key.
System.in.read();
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
log.info("web server stop.");
connector.stop();
}
}
}
Tomcatμμλ Connector κ°μ²΄λ₯Ό λ§λ€κ³ μ΄λ₯Ό μμν©λλ€.
Connector κ°μ²΄λ λ€μκ³Ό κ°μ΄ ꡬνλμ΄ μμ΅λλ€.
public class Connector implements Runnable {
private static final int DEFAULT_PORT = 8080;
private static final int DEFAULT_ACCEPT_COUNT = 100;
private final ServerSocket serverSocket;
private boolean stopped;
public Connector() {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT);
}
public Connector(final int port, final int acceptCount) {
this.serverSocket = createServerSocket(port, acceptCount);
this.stopped = false;
}
private ServerSocket createServerSocket(final int port, final int acceptCount) {
try {
final int checkedPort = checkPort(port);
final int checkedAcceptCount = checkAcceptCount(acceptCount);
return new ServerSocket(checkedPort, checkedAcceptCount);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private int checkPort(final int port) {
final var MIN_PORT = 1;
final var MAX_PORT = 65535;
if (port < MIN_PORT || MAX_PORT < port) {
return DEFAULT_PORT;
}
return port;
}
private int checkAcceptCount(final int acceptCount) {
return Math.max(acceptCount, DEFAULT_ACCEPT_COUNT);
}
public void start() {
var thread = new Thread(this);
thread.setDaemon(true);
thread.start();
stopped = false;
log.info("Web Application Server started {} port.", serverSocket.getLocalPort());
}
@Override
public void run() {
// ν΄λΌμ΄μΈνΈκ° μ°κ²°λ λκΉμ§ λκΈ°νλ€.
while (!stopped) {
connect();
}
}
private void connect() {
try {
Socket connection = serverSocket.accept();
process(connection);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
private void process(final Socket connection) {
if (connection == null) {
return;
}
Http11Processor processor = new Http11Processor(connection);
new Thread(processor).start();
}
public void stop() {
stopped = true;
try {
serverSocket.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
κ·Έλ₯ μ΄λ €μ΄ μ½λλ μλλ―λ‘ μ€λͺ μ μλ΅ν©λλ€.
connect()μ process() λ©μλκ° μ€μνλ°μ, μ΄λ€μ λν΄μλ μλμμ μ€λͺ μ μ΄μ΄κ°λλ‘ νκ³ , κ·Έ μ μ Http11Processor μ½λλ₯Ό μ΄ν΄λ³΄κ² μ΅λλ€.
public class Http11Processor implements Runnable, Processor {
private final Socket connection;
public Http11Processor(final Socket connection) {
this.connection = connection;
}
@Override
public void run() {
log.info("connect host: {}, port: {}", connection.getInetAddress(), connection.getPort());
process(connection);
}
@Override
public void process(final Socket connection) {
try (final var inputStream = connection.getInputStream();
final var outputStream = connection.getOutputStream()) {
// TODO
outputStream.write(response.getBytes());
outputStream.flush();
} catch (IOException | UncheckedServletException e) {
log.error(e.getMessage(), e);
}
}
}
μ΄λ κ² ν΄μ μμΌλ‘ ꡬνν μ½λμ κΈ°μ΄ λΌλ μ½λλ₯Ό μ΄ν΄λ³΄μμ΅λλ€.
μ΄μ μΉ μλ²μ μμ² μ²λ¦¬ νλ‘μ°λ₯Ό λ°λΌ κ·Έ κ³Όμ μμ μ΄λ€ μ½λλ€μ΄ μ¬μ©λλμ§ μ΄ν΄λ³΄λλ‘ νκ² μ΅λλ€.
π³ 1. ν΄λΌμ΄μΈνΈ 컀λ₯μ μλ½
μμ²μ μμμ ν΄λΌμ΄μΈνΈκ° μΉ μλ²λ‘ μμ²μ 보λ΄λ κ²μ΄λ©°, μΉ μλ²λ ν΄λΌμ΄μΈνΈλ‘λΆν° μ μ μμ²μ΄ μ€λ©΄ μ΄λ₯Ό μλ½ν΄μ£Όμ΄μΌ λ°μ΄ν°λ₯Ό μ£Όκ³ λ°μ μ μμ΅λλ€.
μ΄ λΆλΆμ μλμ κ°μ΄ ꡬνλ©λλ€.
private void connect() {
try {
// serverSocketμ ServerSocket νμ
Socket connection = serverSocket.accept();
process(connection);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
μ°μ ServerSocketμ accept()λ₯Ό μ΄ν΄λ³΄κ² μ΅λλ€.
μ£Όμμ ν΅ν΄ ν΄λΉ λ©μλλ ServerSocketμ λν μ°κ²°(connection)μ΄ μ΄λ£¨μ΄μ§ λ κΉμ§ κΈ°λ€λ Έλ€κ°, μ°κ²°μ μλ½νκ³ μλ‘μ΄ Socketμ μμ±νμ¬ λ°ννλ€κ³ λμμμ΅λλ€.
κ·Έλ°λ° μ Socketμ μλ‘ λ§λ€μ΄ λ°ννλ κ²μΌκΉμ?
λ€νΈμν¬μ λν μ§μμ΄ μ΄λμ λ μμΌμ λΆμ΄λΌλ©΄ κΈλ°© μμΈ‘νκ² μ§λ§, μ μ κ°μ΄ μ§μμ΄ μλ μ¬λλ€μ μν΄ (λ―Έλμ μ λ₯Ό μν΄) μμΈν μ€λͺ μ μ§ννλλ‘ νκ² μ΅λλ€.
μ΄λ₯Ό μ΄ν΄νκΈ° μν΄μλ μΉ μμ²μ΄ μ΄λ ν κ³Όμ μ ν΅ν΄ μ΄λ£¨μ΄μ§λμ§μ λν΄μ μμμΌ ν©λλ€.
맨 μ²μ μ¬μ©μκ° λΈλΌμ°μ λ₯Ό ν΅ν΄ μλ²μ μμ²μ 보λ΄λ κ²½μ°, μλ²μ URL, Http Methodμ μ λ ₯λ λ°μ΄ν° λ±μ λ°νμΌλ‘ λΈλΌμ°μ λ HTTP 리νμ€νΈ λ©μΈμ§λ₯Ό λ§λλλ€.
μ΄ν DNS μλ²λ‘λΆν° μλ²μ IP μ£Όμλ₯Ό μ‘°ννκ³ , μ΄κ³³μ μμ² λ©μΈμ§λ₯Ό 보λ΄κ² λ©λλ€.
μ΄λ λ°μνλ λ°μ΄ν°μ μ‘βμμ λμμ κ·Έλ¦ΌμΌλ‘ νννλ©΄ λ€μκ³Ό κ°μ΅λλ€.
λ°μ΄ν°λ₯Ό μ‘βμμ νλ μ»΄ν¨ν° μ¬μ΄μ λ°μ΄ν°κ° νλ₯΄λ ν΅λ‘κ° μ‘΄μ¬νλ©°, μ΄λ₯Ό ν΅ν΄ λ°μ΄ν°κ° νλ₯΄λ©΄μ μλμΈ‘μ λμ°©νκ² λ©λλ€.
μ΄λ μ΄λ¬ν ν΅λ‘λ μ²μλΆν° μ°κ²°λ μνλ‘ μ‘΄μ¬νμ§ μμ΅λλ€.
λ°μ΄ν°λ₯Ό μ‘βμμ νκΈ° μ μ μ μ»΄ν¨ν° μ¬μ΄μμ ν΅λ‘λ₯Ό μ°κ²°ν΄μ£Όλ μμ μ΄ μ νλμ΄μΌ ν©λλ€.
μ΄λ ν΄λΉ ν΅λ‘μ μ λμ μ‘΄μ¬νλ λ°μ΄ν°μ μΆμ ꡬλ₯Ό μμΌμ΄λΌκ³ λΆλ¦ λλ€.
ν΅λ‘λ₯Ό μ°κ²°νκΈ° μν΄μλ μ΄ μμΌμ λ¨Όμ λ§λ€μ΄μΌ νκ³ , μ΄ν μμͺ½μ μμΌμ μ°κ²°νλ μμ μ΄ νμν©λλ€.
μ΄λ μ€μ ꡬνμ μμ μ½λμμ λ³Έ κ²κ³Ό κ°μ΄, μλ²μμ λ¨Όμ μμΌμ λ§λ λ€ ν΄λΌμ΄μΈνΈμμ μ°κ²°νκΈ°λ₯Ό κΈ°λ€λ¦¬κ² λ©λλ€.
(ServerSocketμ accpet() λ©μλ μ€λͺ κ³Ό λμΌν©λλ€.)
μ΄ν ν΄λΌμ΄μΈνΈμΈ‘μμ μμΌμ λ§λ λ€, μλ²μ μμΌκ³Ό μ°κ²°νκ² λλ©΄ κ·ΈλλΆν° λ°μ΄ν°λ₯Ό μ£Όκ³ λ°μ μ μκ² λ©λλ€.
μ΄λ₯Ό ν΅ν΄ μ μ μλ κ²μ λ€μκ³Ό κ°μ΅λλ€.
λ°μ΄ν°λ₯Ό μ£Όκ³ λ°κΈ° μν΄μλ ν΄λΌμ΄μΈνΈ μμΌμ΄ μλ² μμΌμ μ°κ²°λμ΄μΌ ν©λλ€.
κ·Έλ€μμΌλ‘ μμλ³Ό κ²μ, ServerSocketκ³Ό Socketμ λν λ΄μ©μ
λλ€.
λ²μ¨ λμΉ μ²΄μ
¨μ μλ μμ§λ§, κ°κ°μ ν΄λμ€μ μ€λͺ
μ νλ² μ΄ν΄λ³΄λλ‘ νκ² μ΅λλ€.
λ¨Όμ ServerSecketμ μ€λͺ μ λ€μκ³Ό κ°μ΅λλ€.
This class implements server sockets.
A server socket waits for requests to come in over the network.
It performs some operation based on that request, and then possibly returns a result to the requester.
The actual work of the server socket is performed by an instanceof the {@code SocketImpl} class.
An application can change the socket factory that creates the socket implementation to configure itself to create sockets appropriate to the local firewall.
첫 μ€μμ μ μ μλ―μ΄ ServerSocketμ μλ²μ socketμ ꡬνν©λλ€.
κ·Έμ λ°μ Socketμ μ€λͺ μ λ€μκ³Ό κ°μ΅λλ€.
This class implements client sockets (also called just "sockets").
A socket is an endpoint for communication between two machines.
The actual work of the socket is performed by an instance of the SocketImpl class.
An application, by changing the socket factory that creates the socket implementation, can configure itself to create sockets appropriate to the local firewall.
μ¦ ServerSocketμ accpet()μμ λ°νν΄μ£Όλ Socketμ μλ² μμΌκ³Ό μ°κ²°λ ν΄λΌμ΄μΈνΈ μμΌμμ μ μ μμ΅λλ€.
π³ 2, 3. μμ² λ©μΈμ§ μμ & μ²λ¦¬
ν΄λΌμ΄μΈνΈκ° λ³΄λΈ HTTP μμ² λ©μμ§λ₯Ό λ€νΈμν¬λ‘λΆν° μ½μ΄μΌ ν©λλ€.
κ·Έ κ³Όμ μ λ€μκ³Ό κ°μ΄ ꡬνλ©λλ€.
private void process(final Socket connection) {
if (connection == null) {
return;
}
var processor = new Http11Processor(connection);
new Thread(processor).start();
}
public class Http11Processor implements Runnable, Processor {
private static final Logger log = LoggerFactory.getLogger(Http11Processor.class);
private final Socket connection;
public Http11Processor(final Socket connection) {
this.connection = connection;
}
@Override
public void run() {
log.info("connect host: {}, port: {}", connection.getInetAddress(), connection.getPort());
process(connection);
}
@Override
public void process(final Socket connection) {
try (final var inputStream = connection.getInputStream();
final var outputStream = connection.getOutputStream()) {
String request = μμ²_λ©μΈμ§_μμ (inputStream);
// μλ λκ°κ° 'μλ΅ μμ±'
String responseBody = λ©μμ§μμ_μ§μ ν_리μμ€_κ°μ Έμ¨λ€(request);
String response = μλ΅_μμ±();
// μλ λκ°κ° 'μλ΅ λ°ν'
outputStream.write(response.getBytes());
outputStream.flush();
} catch (IOException | UncheckedServletException e) {
log.error(e.getMessage(), e);
}
}
}
μ΄λ μμ² λ©μΈμ§λ₯Ό μμ νλ λΆλΆμ λ€μκ³Ό κ°μ΅λλ€.
String request = μμ²_λ©μΈμ§_μμ (inputStream);
InputStreamμ΄λ κ²μμ μμ² λ©μΈμ§λ₯Ό μμ ν©λλ€.
π³ 4. 리μμ€ λ§€νκ³Ό μ κ·Ό
private String λ©μμ§μμ_μ§μ ν_리μμ€_κ°μ Έμ¨λ€(request){
String 리μμ€_μ΄λ¦ = λ©μμ§μμ_μ§μ ν_리μμ€_μ΄λ¦μ_κ°μ Έμ¨λ€(request);
return resources_λλ ν 리_λ΄μμ_리μμ€λ₯Ό_μ°Ύμ_λ°ννλ€(리μμ€_μ΄λ¦);
}
μ΄λ μΉ μλ²μ resources λλ ν 리μλ λ€μκ³Ό κ°μ μ μ νμΌλ€μ΄ μ‘΄μ¬ν©λλ€.
μ¦ μμ²μ΄ localhost:8080/index.htmlμΈ κ²½μ° Index.htmlμ μ°Ύμ λ°νν΄ μ£Όμ΄μΌ νλ κ²μ λλ€.
π³ 5. μλ΅ λ©μΈμ§ μμ±
// μλ λκ°κ° 'μλ΅ μμ±'
String responseBody = λ©μμ§μμ_μ§μ ν_리μμ€_κ°μ Έμ¨λ€(request);
String response = μλ΅_μμ±();
μλ΅ ν€λλ₯Ό ν¬ν¨ν HTTP μλ΅ λ©μΈμ§λ₯Ό μμ±ν΄μΌ ν©λλ€.
HTTP μλ΅ λ©μΈμ§λ λ€μκ³Ό κ°μ ννμ λλ€.
π³ 6. μλ΅ λ°ν
// μλ λκ°κ° 'μλ΅ λ°ν'
outputStream.write(response.getBytes());
outputStream.flush();
λμ§ λͺ¨λ₯΄κ²μ§λ§ OutputStreamμ΄λ κ²μ ν΅ν΄μ μλ΅μ λ°νν©λλ€.
μ΄ν μ°κ²°μ΄ μ’ λ£λ©λλ€.
π³ 7. μ°κ²° μ’ λ£
try (final var inputStream = connection.getInputStream();
final var outputStream = connection.getOutputStream()) {
} // try with resource λ₯Ό ν΅ν auto close
try with resourceλ₯Ό μ¬μ©νμ¬ OutputStreamμ΄ close λλ©΄μ μ°κ²°μ΄ μ’ λ£λ©λλ€.
connection.getOutputStream()μ νμ μ SocketOutputStreamμΈλ°μ, SocketOutputStreamμ close() λ©μλλ λ€μκ³Ό κ°μ΅λλ€.
μμΌμ λ«μμ€μΌλ‘μ¨ μ°κ²°μ μ’ λ£νλ μ½λκ° λ€μ΄μλ κ²μ νμΈν μ μμ΅λλ€.
μ΄λ κ² ν΄μ μΉ μλ²μ κΈ°λ³Έμ μΈ λμκ³Ό νλ‘μ°λ₯Ό μμ보μκ³ , κ·Έ νλ‘μ° λ΄μμ λͺλͺ μ½λλ€μ μ΄ν΄λ³΄λ©° μ 체μ μΈ νλ¦μ νμ ν μ μμμ΅λλ€.
λ€μ κΈμμλΆν°λ μ μ½λ μ€μμ μλ¬Έμ΄ μκ²Όλ λ€μ λΆλΆλ€μ λν΄ μ 리νλλ‘ νκ² μ΅λλ€.
- InputStream, OutputStreamμ΄ λμ§?
- resources λλ ν 리 λ΄μ νμΌλ€μ μ΄λ»κ² κ°μ ΈμμΌνμ§?