[rfc] User-Agent란

HTTP 2019. 12. 24. 18:06

서버에서 Mac OS X 환경에서 요청시 생기는 보안 이슈 때문에 따로 관리하기 위해 식별해내려 한다. 이 때, User-Agent를 확인하여 알 수 있을 것 같아 rfc문서의 User-Agent 부분을 조사하려 한다.

-@참고 : https://tools.ietf.org/html/rfc7231#section-5.5.3

 

RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content

[Docs] [txt|pdf] [draft-ietf-http...] [Tracker] [Diff1] [Diff2] [Errata] PROPOSED STANDARD Errata Exist Internet Engineering Task Force (IETF) R. Fielding, Ed. Request for Comments: 7231 Adobe Obsoletes: 2616 J. Reschke, Ed. Updates: 2817 greenbytes Catego

tools.ietf.org

5.5.3. User-Agent

"User-Agent" 헤더 필드는 요청에 기반한 사용자 에이전트에 관한 정보를 포함하며, 보고된 상호운용성 문제의 범위를 식별하고, 특정 사용자 에이전트 제한을 피하기 위해 응답을 조정하거나 재단하고, 브라우저나 운영체제 사용에 관한 분석을 위해 종종 서버에 의해 사용된다. 사용자 에이전트는 특별히 구성하지 않는 한 각 요청에 대하여 User-Agent 필드를 보내야 한다.

User-Agent = product * (RWS (product / comment))

User-Agent 필드-값은 하나 이상의 제품 식별자로 구성되어 있으며, 각각은 0개 이상의 주석 ([RFC7230] 섹션 3.2)이 있고, 사용자 에이전트 소프트웨어와 중요한 하위 제품을 함께 식별한다. 일반적으로 제품 식별자는 사용자 에이전트 소프트웨어를 식별하는 중요성에 대한 내림차순으로 나열되어있다. 각 제품 식별자는 이름과 선택적 버전으로 구성되어 있다.

product            = token ["/" product-version]

product-version  = token

발신자는  생산된 제품 식별자를 제품을 식별하는데 필요한 것으로 제한해야한다. 발신자는 제품 식별자 내에 광고나 기타 불필요한 정보를 생성하면 안된다. 발신자는 버전 식별자가 아닌 제품 버전 정보를 생성하면 안된다. (즉, 동일한 제품 이름의 연속 버전은 제품 식별자의 제품 버전 부분에서만 달라야 한다.)

예:

User-Agent: CERN-LineMode/2.15 libwww/2.17b3

사용자 에이전트는 불필요하게 세밀한 디테일을 사용자 에이전트 필드를 생성해서는 안되고, 제3자에 의한 하위제품의 추가를 제한해야된다. 너무 길고 자세한 사용자 에이전트 필드 값은 요청 지연과 원하는 바("지문")에 반하여 식별되는 사용자 리스크를 증가시킨다.

마찬가지로, 필드의 목적에 우회하므로 호환성을 선언하기 위해 다른 구현의 제품 토큰을 사용하지 않는 구현이 장려되어진다. 사용자 에이전트가 다른 사용자 에이전트로 가장하는 경우, 수신자는 실제 사용자 에이전트에서 제대로 작동하지 않더라도 식별된 사용자 에이전트에 맞게 조정된 응답을 의도적으로 보고 싶어한다고 가정할 수 있다.

'HTTP' 카테고리의 다른 글

[Web, CORS] SameSite Cookie 란  (0) 2022.02.08
Java에서 InputStream을 읽고 String으로 변환하는 방법  (0) 2020.04.24
블로그 이미지

uchacha

개발자 일지

,

스프링 시큐리티를 데모 애플리케이션을 만드는 중에 

ERROR: org.springframework.web.servlet.tags.form.ErrorsTag - No message found under code 'duplicate.newUser.name' for locale 'ko_KR'.
org.springframework.context.NoSuchMessageException: No message found under code 'duplicate.newUser.name' for locale 'ko_KR'.

이러한 에러메세지를 맞닥뜨리게 되어 messageSource에 알아봐야 할 필요성을 느껴 관련 문서를 번역해보게 되었습니다.

-@참고 : http://zetcode.com/spring/messagesource/

 

Spring MessageSource tutorial - using MessageSource in a Spring application

Home Subscribe Spring MessageSource tutorial Spring MessageSource tutorial shows how to translate messages using MessageSource in a Spring application. Spring is a popular Java application framework for creating enterprise applications. Spring MessageSourc

zetcode.com

스프링 메세지소스 튜토리얼은 스프링 어플리케이션에서 메세지소스를 사용하여 메세지를 번역하는 방법을 보여줄 것입니다.

스프링은 엔터프라이즈 어플리케이션을 만드는데 널리 사용되는 자바 어플리케이션 프레임워크입니다.

스프링 메세지소스

MesssageSource는 메세지의 매개 변수화 및 국제화를 지원하여 메세지를 해결하는데 사용됩니다. 스프링은 두 개의 내장 MessageSource 구현을 갖습니다. 바로 ResourceBundleMessageSoource와 ReloadableResourceBundleMessageSource입니다. 후자는 가상 머신의 재시작 없이 메세지 정의를 리로드 할 수 있습니다.

스프링 메세지소스 예제

다음 애플리케이션은 영어와 독일어 메세지를 포함하고 있습니다. 내장 ResourceBundleMesssageSource를 사용합니다.

pom.xml
src
├───main
│   ├───java
│   │   └───com.test.messagesource
│   │           │   Application.java
│   │           └───config
│   │                   AppConfig.java
│   └───resources
│       │   logback.xml
│       └───messages
│               label.properties
│               label_de.properties
└───test
    └───java

이것이 프로젝트 구조입니다.

📄 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>messagesource</artifactId>
	<name>MessageSourceTest</name>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	
	<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>11</maven.compiler.source>
		<maven.compiler.target>11</maven.compiler.target>
		<org.springframework-version>5.1.3.RELEASE</org.springframework-version>
	</properties>
	
	<dependencies>
		<!-- Logging -->
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
		</dependency>
		
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
	</dependencies>
	
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

pom.xml 파일에는 기본 스프링 의존성인 spring-core, spring-context와 로깅에 관한 logback-classic 의존성이 있습니다.

exec-maven-plugin은 메이븐에서 커맨드로 스프링 애플리케이션을 실행하는데 사용됩니다.

📄 resource/logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<logger name="org.springframework" level="ERROR" />
	<logger name="com.test" level="INFO" />
	
	<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<Pattern>%d{HH:mm:ss.SSS} %blue(%-5level) %magenta(%logger{36}) - %msg %n</Pattern>
		</encoder>
	</appender>
	
	<root>
		<level value="INFO" />
		<appender-ref ref="consoleAppender" />
	</root>
</configuration>

logback.xml은 로그백 로깅 라이브러리에 대한 구성파일입니다.

📄 resources/messages/label.properties

l1=Earth
l2=Hello {0}, how are you?

이것은 영어 메세지입니다. 두번째 속성은 매개 변수를 받습니다.

📄 resources/messages/label_de.properties

l1=Erde
l2=Hallo {0}, wie geht's?

이것은 독일어 메세지입니다.

📄 com/test/messagesource/config/AppConfig.java

package com.test.messagesource.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;

@Configuration
public class AppConfig {
	
	@Bean
	public ResourceBundleMessageSource messageSource() {
		
		ResourceBundleMessageSource source= new ResourceBundleMessageSource();
		source.setBasenames("messages/label");
		source.setUseCodeAsDefaultMessage(true);
		
		return source;
	}
}

AppConfig는 ResourceBundleMessage를 구성합니다. setBasenames()는 메세지 정의를 찾을 위치를 알려줍니다.

📄 com/test/messagesource/Application.java

package com.test.messagesource;

import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "com.test.messagesource")
public class Application {
	
	private static final Logger logger = LoggerFactory.getLogger(Application.class);
	
	@Autowired
	private MessageSource messageSource;
	
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class);
		Application app = ctx.getBean(Application.class);
		app.run();
		
		ctx.close();
	}

	public void run() {
		logger.info("Translated messages:");
		logger.info("{}", messageSource.getMessage("l1", null, Locale.GERMAN));
		logger.info("{}", messageSource.getMessage("l1", null, Locale.ENGLISH));
		
		logger.info("Translated parameterized messages:");
		logger.info("{}", messageSource.getMessage("l2", new Object[] {"Paul Smith"}, Locale.GERMAN));
		logger.info("{}", messageSource.getMessage("l2", new Object[] {"Paul Smith"}, Locale.ENGLISH));
	}
}

 이 어플리케이션은 콘솔에 일반 메세지와 매개변수가 있는 메세지를 프린트합니다.

@Autowired
private MessageSource messageSource;

AppConfig에서 생성된 MessageSource를 주입합니다.

logger.info("{}", messageSource.getMessage("l1", null, Locale.GERMAN));

getMessage()는 프로퍼티 이름을 첫번째 매개변수로 사용합니다. 메세지에 매개변수가 없으므로 두번째 매개변수는 널입니다. 세번째 매개변수는 해당 로케일입니다.

logger.info("{}", messageSource.getMessage("l2", new Object[] {"Paul Smith"}, Locale.GERMAN));

여기서는 메세지에 매개변수를 제공합니다.

💻 출력

11:36:39.541 INFO  com.test.messagesource.Application - Translated messages: 
11:36:39.544 INFO  com.test.messagesource.Application - Erde 
11:36:39.545 INFO  com.test.messagesource.Application - Earth 
11:36:39.545 INFO  com.test.messagesource.Application - Translated parameterized messages: 
11:36:39.546 INFO  com.test.messagesource.Application - Hallo Paul Smith, wie gehts? 
11:36:39.546 INFO  com.test.messagesource.Application - Hello Paul Smith, how are you? 

응용 프로그램을 실행합니다.

이 튜토리얼에서는 스프링 어플리케이션에서 ResourceBundleMessage를 사용하는 방법을 보여줬습니다.

블로그 이미지

uchacha

개발자 일지

,

- @참고 : https://refactoring.guru/design-patterns/template-method

 

Template Method

Problem Imagine that you’re creating a data mining application that analyzes corporate documents. Users feed the app documents in various formats (PDF, DOC, CSV), and it tries to extract meaningful data from these docs in a uniform format. The first versio

refactoring.guru

현재 공부 중인 스프링 시큐리티에서 위임에 대한 내용이 나와 공부하는 도중에 상속과 위임의 관계에 대해서 Template Method 패턴에 등장한다고 하여 살펴보려합니다.

🙌 취지

Template Method는 상위클래스에 대한 알고리즘의 골격을 정의하는 행동 디자인 패턴이지만 서브클래스가 특정 알고리즘 스텝에 따라 그 구조를 바꾸지 않고 오버라이드 할 수 있게 합니다.

🤔 문제점

당신이 기업 문서를 분석하는 데이터 마이닝 어플리케이션을 만든다고 상상해보세요. 사용자는 다양한 포맷(PDF, DOC, CSV)으로 문서를 생산하고, 앱은 의미있는 데이터를 일정한 포맷으로 뽑아내려 합니다.

첫번째 버전은 DOC 파일일 경우에만 작동합니다. 다음 버전에는 CSV 파일도 지원합니다. 한달 뒤 당신은 PDF 파일로 데이터를 추출하는 방법을 "가르쳤습니다".

많은 중복된 코드를 포함하는 데이터 마이닝 클래스들

어느 시점에서, 당신은 이 세가지 클래스들이 많은 비슷한 코드를 갖고 있다는 것을 알아챘습니다. 다양한 포맷의 데이터를 다루는 코드는 각 클래스에서 완전히 다른 반면에, 데이터를 처리하고 분석하는 코드는 거의 동일합니다. 그렇다면 알고리즘 구조는 그대로 놔두면서 중복 코드를 제거하는게 더 낫지 않겠습니까?

이 클래스에 사용된 클라이언트 코드에 또 다른 문제점이 있습니다. 처리 객체의 클래스에 따라 적절한 조치를 취한 많은 조건이 있습니다. 만약 이 세 처리 클래스들이 공통 인터페이스나 기본 클래스를 갖는다면 당신은 클라이언트 코드에서 조건을 제거할 수 있고 처리 오브젝트에서 메소드를 호출할 때 다형성을 사용할 수 있습니다.

😊 해결책

Template Method 패턴은 알고리즘을 일련의 단계로 구분짓고 이 단계를 메소드로 바꾸고, 하나의 "템플릿 메소드" 안에서 이러한 메소드에 대한 일련의 호출을 넣는 것을 제안합니다. 이 단계는 추상적이거나, 일부 기본 구현이 있을 수도 있습니다. 알고리즘을 사용하기 위해 클라이언트는 자체 서브클래스를 제공해야 하고, 모든 추상 단계를 구현하고, 필요시(템플릿 메소드 자체에서는 아니지만) 어떤 선택적 단계를 오버라이드해야 한다.

이것이 우리의 데이터 마이닝 앱에서 어떻게 작동할지 봅시다. 우리는 모든 세개의 파싱 알고리즘에 대한 기본 클래스를 생성할 수 있습니다. 이 클래스는 다양한 문서 처리 단계에 대한 일련의 호출로 구성된 템플릿 메소드를 정의합니다.

Template method는 알고리즘을 단계별로 구분하여 서브클래스가 이 단계를 실제 메소드는 아니지만 오버라이드하게 합니다.

첫째, 모든 단계를 abstract로 선언합니다. 이는 서브클래스가 자체 구현을 이 메소드로 구현하게끔 합니다. 우리의 경우, 서브클래스가 이미 모든 필요한 구현을 하고 있기 때문에 우리는 상위클래스의 메소드에 맞게 메소드의 signature만 조정하면 됩니다.

이제, 중복코드를 없애기 위해 무엇을 해야하는지 봅시다. 파일 열기/닫기나 데이터 추출하기/파싱하기는 각각의 데이터 포맷마다 달라보입니다. 따라서 이 메소드들에 대해서는 건드릴 곳이 없습니다. 그러나 원시 데이터를 분석하거나 보고서 작성과 같은 다른 단계의 구현은 매우 비슷하므로 서브 클래스가 코드를 공유할 수 있게 기본 클래스로 가져올 수 있습니다.

보시다시피, 우리는 두가지 단계를 거칩니다 :

  • 추상 단계는 모든 서브클래스에서 구현되어야 합니다.
  • 선택적 단계는 이미 일부 기본 구현을 갖지만, 필요하다면 오버라이드 될 수 있습니다.

또 다른 단계가 있는데 hooks라 불립니다. hook는 바디가 비어있는 선택적 단계입니다. 템플릿 메소드는 hook이 오버라이드 되지 않아도 작동할 수 있습니다. 일반적으로 hook은 중요한 알고리즘 스텝 전이나 후에 위치되어, 추가적인 알고리즘 확장 포인트를 서브클래스에 제공합니다.

🚗 실세계에 비유

일반적인 건축 계획이 고객의 요구에 더 잘 맞도록 약간 변형될 수 있습니다.

템플릿 메소드적 접근은 대량 주택 건설에 사용될 수 있습니다. 표준 주택을 짓기위한 건축 계획은 몇가지 확장점들을 갖고 있고 소유자들이 결과 주택의 일부 세부사항을 조정할 수 있게 합니다.

토대를 다지고, 구조를 짜고, 벽을 세우고, 배선을 설치하고, 수도 및 전기를 연결하는 등의 각각의 건축 단계는 결과 주택을 다른 건물과 약간 다르게 만들며 약간의 변화가 있을 수 있습니다.

🔦구조

1. 추상 클래스(Abstract Class)는 특정 단계에서 이러한 메소드를 호출하는 실제 템플릿 메소드 뿐만이 아니라 알고리즘의 단계로 작동하는 메소드를 선업합니다. 이 단계는 추상적이거나 일부 기본 구현을 갖도록 선언 될 수 있습니다.

2. 구체적 클래스(Concrete Classes)는 모든 단계를 오버라이드 할 수 있지만 그것은 템플릿 메소드는 아닙니다.

 

 

 

 

 

# 짜가코드

이 예제에서, Template Method 패턴은 간단한 전략 비디오 게임에서 다양한 인공지능 분기에 대한 "골격"을 제공합니다. 

간단한 비디오 게임의 AI 클래스들

게임의 모든 종족은 거의 같은 유닛과과 건물을 가지고 있습니다. 그러므로 당신은 동일한 AI 구조를 여러 종족에서 재사용하며, 약간의 디테일만 다르게 오버라이드 할 수 있습니다. 이 방법을 사용하여, 당신은 오크 AI를 좀 더 공격적으로 재정의 할 수 있고, 휴먼은 좀 더 방어적으로, 몬스터는 건축을 할 수 없게 만들 수 있습니다. 게임에 새 종족을 추가하려면 새로운 AI 서브클래스를 만들고 기본 AI 클래스에 선언된 기본 메소드를 재정의해야 합니다.

/*
추상 클래스는 일반적으로 기본 작업을 추상화하기 위해 호출로 구성된 일부 알고리즘 골격을 포함하는 템플릿 메소드를 정의합니다. 구체적 서브 클래스는 이 작업을 구현하지만, 템플릿 메소드 자체는 그대로 둡니다.
*/
class GameAI is
	//템플릿 메소드는 알고리즘의 뼈대를 정의합니다.
	method turn() is
    	collectResources()
        buildStructures()
        buildUnits()
        attack()
    
    //일부 단계는 기본 클래스에서 바로 구현될 수도 있습니다.
    method collectResources() is
    	foreach (s in this.buildStructures) do
        	s.collect()
            
   	//그리고 몇몇은 추상적으로 정의되어 집니다.
    abstract method buildStructures()
    abstract method buildUnits()
    
    //클래스는 여러 템플릿 메소드를 가질 수 있습니다.
    method attack() is
    	enemy = closestEnemy()
        if (enemy == null)
        	sendScouts(map.center)
        else
        	sendWarriors(enemy.position)
            
    abstract method sendScouts(position)
    abstract method sendWarriors(position)
    
/*
구체적 클래스는 기본 클래스의 모든 추상 작업을 구현해야 하지만 템플릿 메소드 자체를 재정의해서는 안됩니다.
*/
class OrcsAI extends GameAI is
	method buildStructures() is
    	if (there are some resources) then
        	//농장을 짓고, 막사를 짓고, 요새를 건설합니다.
            
    method buildUnits() is
    	if (there are plenty of resources) then
        	if (there are no scouts)
            	//보병을 만들고 정찰 그룹에 추가합니다.
            else 
            	//대원을 만들고 전사 그룹에 추가합니다.
                
    // ...
    
    method sendScouts(position) is
    	if (scouts.length > 0) then
        	// 그 위치로 정찰을 보냅니다.
            
    method sendWarriors(position) is
    	if (warriors.length > 5) then
        	// 그 위치로 전사를 보냅니다.
            
// 서브클래스는 기본 구현으로 일부 조작을 재정의할 수 있습니다.
class MonstersAI extends GameAI is
	method collectResources() is
    	// 몬스터는 자원을 모으지 않습니다.
        
	method buildStructures() is
    	// 몬스터는 구조물을 건축하지 않습니다.
        
	method buildUnits() is
    	// 몬스터는 유닛을 생산하지 않습니다.
            
 

💡 적용

  • 클라이언트가 알고리즘의 어떤 특정 단계만을 확장하고 전체 알고리즘이나 구조는 확장하지 못하게 하고 싶을 때 템플릿 메소드를 사용합니다.
  • 템플릿 메소드는 획일적 알고리즘을 상위클래스에서 정의된 구조는 그대로 유지하면서 서브클래스에 의해 쉽게 확장되는 일련의 개별 단계로 전환 할 수 있습니다.
  • 거의 동일한 알고리즘을 포함한 여러 클래스가 있는 경우 이 패턴을 사용합니다. 그 결과로, 당신은 알고리즘을 변경할 때 모든 클래스를 수정해야할 수도 있습니다.
  • 이러한 알고리즘을 템플릿 메소드로 전환하면, 당신은 상위클래스로 유사한 구현 단계를 끌어 올려 코드 중복을 제거할 수 있습니다. 하위클래스마다 다른 코드는 하위클래스에 남아있을 수 있습니다.

📋 구현 방법

  1. 단계적으로 쪼갤 수 있는지 대상 알고리즘을 분석합니다. 어떤 단계가 모든 서브 클래스에 공통인지 어떤 단계가 고유한지를 고려합니다.
  2. 추상 기본 클래스를 만들고 알고리즘 단계를 나타내는 템플릿 메소드와 추상 메소드 집합을 선언합니다. 해당 단계를 실행하여 템플릿 메소드의 알고리즘 구조를 윤곽짓습니다. 하위클래스에서 오버라이드하는걸 방지하기 위해 템플릿 메소드를 final로 설정하십시오.
  3. 모든 단계가 추상적이어도 괜찮습니다. 그러나 일부 단계는 기본 구현으로 이점을 얻을 수 있습니다. 서브클래스가 이러한 메소드를 구현할 필요가 없습니다.
  4. 알고리즘의 중요 단계 사이에 hook을 추가하는 걸 고려하십시오.
  5. 알고리즘의 각 변형에 대해 새 구체적 서브클래스를 작성하십시오. 모든 추상 단계를 구현해야하지만, 일부 선택적 단계를 오버라이드 할 수도 있습니다.

⚖ 장단점

✔ 당신은 클라이언트가 커다란 알고리즘의 특정 부분만 오버라이드하게 하여 알고리즘의 다른 부분에서 발생하는 변경의 영향을 덜받게 할 수 있습니다.

✔ 당신은 중복 코드를 상위클래스로 당겨올 수 있습니다.

❌ 일부 클라이언트는 제공된 알고리즘 골격에 의해 제한될 수 있습니다.

❌ 당신은 하위 클래스를 통한 기본 단계 구현를 억제하여 Liskov 대체 원칙을 위반할지 모릅니다.

❌ 템플릿 메소드는 더 많은 단계를 갖게 될 수록 유지하기 어려운 경향이 있습니다.

🐱‍🏍 다른 패턴과의 관계

  • 팩토리 메소드템플릿 메소드의 전문화입니다. 동시에 팩토리 메소드는 큰 템플릿 메소드의 단계 역할을 할 수 있습니다.
  • 템플릿 메소드는 상속에 기반합니다. 하위클래스에서 해당 파트를 확장하여 알고리즘의 일부를 변경할 수 있습니다. 전략은 구성을 기반으로 합니다. 당신은 해당 동작에 해당하는 다른 전략을 제공하여 오브젝트 동작의 일부를 변경할 수 있습니다. 템플릿 메소드는 클래스 레벨에서 작동하며, 정적(static)입니다. 전략은 개체 수준에서 작동하므로 런타임시 동작을 전환할 수 있습니다.

</> 코드 예제

이 예제에서, 템플릿 메소드 패턴은 소셜 네트워크 알고리즘을 정의합니다. 특정 소셜 네트워크와 매치되는 서브클래스는 소셜 네트워크에서 제공하는 API를 따라 이 단계를 구현합니다.

📁 networks

📄 networks/Network.java: Base social network class

package com.test.example.networks;

/**
 * Base class of social network.
 *
 */
public abstract class Network {
	String userName;
	String password;
	
	Network() {}
	
	/**
	 * Publish the data to whatever network.
	 */
	public boolean post(String message) {
		// Authenticate before posting. Every network uses a different authentication method.
		if (logIn(this.userName, this.password)) {
			// Send the post data.
			boolean result = sendData(message.getBytes());
			logOut();
			return result;
		}
		return false;
	}

	abstract boolean logIn(String userName, String password);
	abstract boolean sendData(byte[] bytes);	
	protected abstract void logOut();
}

 

📄 networks/Facebook.java: Concrete social network

package com.test.example.networks;

/**
 * Class of social network
 *
 */
public class Facebook extends Network {
	public Facebook(String userName, String password) {
		this.userName = userName;
		this.password = password;
	}

	@Override
	public boolean logIn(String userName, String password) {
		System.out.println("\nChecking user's parameters");
		System.out.println("Name: " + this.userName);
		System.out.print("Password: ");
		for (int i=0; i<this.password.length(); i++) {
			System.out.print("*");
		}
		simulateNetworkLatency();
		System.out.println("\n\nLogin success on Facebook");
		return true;
	}

	@Override
	public boolean sendData(byte[] data) {
		boolean messagePosted = true;
		if (messagePosted) {
			System.out.println("Message: '" + new String(data) + "' was posted on Facebook");
			return true;
		} else {
			return false;
		}
	}

	@Override
	public void logOut() {
		System.out.println("User: '" + userName + "' was logged out from Facebook");
	}
	
	private void simulateNetworkLatency() {
		try {
			int i = 0;
			System.out.println();
			while (i < 10) {
				System.out.print(".");
				Thread.sleep(500);
				i++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

📄 networks/Twitter.java: One more social network

package com.test.example.networks;

/**
 * Class of social network
 *
 */
public class Twitter extends Network {
	
	public Twitter(String userName, String password) {
		this.userName = userName;
		this.password = password;
	}

	@Override
	public boolean logIn(String userName, String password) {
		System.out.println("\nChecking user's parameters");
		System.out.println("Name: " + this.userName);
		System.out.print("Password: ");
		for (int i=0; i<this.password.length(); i++) {
			System.out.print("*");
		}
		simulateNetworkLatency();
		System.out.println("\n\nLogin success on Twitter");
		return true;
	}

	@Override
	boolean sendData(byte[] data) {
		boolean messagePosted = true;
		if (messagePosted) {
			System.out.println("Message: '" + new String(data) + "' was posted on Twitter");
			return true;
		} else {
			return false;
		}
	}

	@Override
	protected void logOut() {
		System.out.println("User: '" + userName + "' was logged out from Twitter");
		
	}
	
	private void simulateNetworkLatency() {
		try {
			int i = 0;
			System.out.println();
			while (i < 10) {
				System.out.print(".");
				Thread.sleep(500);
				i++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

📄 Demo.java: Client code

package com.test.example;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import com.test.example.networks.Facebook;
import com.test.example.networks.Network;
import com.test.example.networks.Twitter;

/**
 * Demo class. Everything comes together here.
 *
 */
public class Demo {
	public static void main(String[] args) throws IOException {
		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
		Network network = null;
		System.out.print("Input user name: ");
		String userName = reader.readLine();
		System.out.print("Input password: ");
		String password = reader.readLine();
		
		// Enter the message.
		System.out.print("Input message: ");
		String message = reader.readLine();
		
		System.out.println("\nChoose social network for poasting message.\n" +
					"1 - Facebook\n" +
					"2 - Twitter");
		int choice = Integer.parseInt(reader.readLine());
		
		// Create proper network object and send the message.
		if (choice == 1) {
			network = new Facebook(userName, password);
		} else {
			network = new Twitter(userName, password);
		}
		network.post(message);
	}
}

 

📄 OutputDemo.txt: Execution result

Input user name: hong
Input password: 1234
Input message: Hello, World!

Choose social network for poasting message.
1 - Facebook
2 - Twitter
2

Checking user's parameters
Name: hong
Password: ****
..........

Login success on Twitter
Message: 'Hello, World!' was posted on Twitter
User: 'hong' was logged out from Twitter
블로그 이미지

uchacha

개발자 일지

,

1. ctrl + alt + c로 postman 콘솔창을 연다.

2. 메세지가 다음과 같았다.

postman Error: error:0906D06C:PEM routines:PEM_read_bio:no start line

→ 인증서 값이 제대로 되지 않아 읽어올 때 실패하는 것 같다.

3. settings > Certificates > client certificates가 등록되어 있어서 삭제

했더니 잘 동작했다.

블로그 이미지

uchacha

개발자 일지

,

@ 참고 : 내 기억, https://kkangeva.tistory.com/91

 

이클립스 CDT로 C/C++ 개발 시작하기

목차 들어가기 이클립스 CDT 설치하기 이클립스로 C/C++ 프로그램 개발하기 마무리 들어가기 이전 포스팅에서 이클립스에 대한 소개를 해봤다. 이제 본격적으로 CDT를 이용한 개발에 대해서 이야기해보려고 한다...

kkangeva.tistory.com

 

방법.

1. C/C++ 개발자용 이클립스 다운로드

2. 이클립스 설치 후 CDT 플러그인 추가 (선택)

 

[Eclipse에 CDT 플러그인을 추가하는 방법]

#1. Eclipse 메뉴 help > Install New Software 선택

#2. Work with에 https://www.eclipse.org/cdt/downloads.php 에서 알맞는 버전의 p2 software repository 주소 복사 후 붙여넣기

(cf. Eclipse 2018-12 버전이라 "http://download.eclipse.org/tools/cdt/releases/9.6" 복붙)

#3. Select All 후 설치

 

[Eclipse에서 C++ 프로그램을 개발하는 방법]

#1. 인코딩 세팅

- Eclipse 메뉴에서 Preferences 에서 General > Workspace 에서 Text file encoding 항목에 Other: UTF-8 체크

#2. Project 생성

- 네비 Context menu에서 new > Other > C/C++ Project > C++ Managed Build > Project Type : Empty Project & Toolchains : MinGW GCC > Finish!

#3. Source File 생성

- 프로젝트의 Context menu에서 new > Source File > Finish!

#4. 코드 작성 후 실행

- 실행 순서 : 저장(Ctrl + S) > Build > Run(Ctrl + F11)

 

 

'프로그램 설치' 카테고리의 다른 글

Visual Studio Code에서 C/C++ 컴파일 사용법  (0) 2019.06.17
블로그 이미지

uchacha

개발자 일지

,

방법1.

@ 참고 : 내 경험

#1. 삭제 후 재 설치


방법2. 이 방법은 실행했으나 안 먹힘

@ 참고 : https://moomini.tistory.com/65

 

이클립스(Eclipse) 실행시 아무 반응이 없는 오류 해결

(공감과 댓글 하나는 글쓴이에게 큰 힘이 됩니다.) eclipse.exe 파일을 실행하면 이클립스(Eclipse)가 실행이 되지않고, 아무 반응이 없는 경우가 종종 있다. 이 경우 응용 프로그램을 실행할 때 초기화 정보를 담..

moomini.tistory.com

#1. eclipse 폴더(C:\eclipse-jee-2018-09-win32\eclipse) 내 eclipse.ini 파일 열기

#2. jdk 경로 -bin 폴더의 javaw.exe 파일이 있는 경로 복사하기

#3. eclipse.ini 파일의 openFile 항목 밑에  --vm을 입력한 후 복사한 경로를 붙여넣고, 끝에 \javaw.exe를 붙여넣기

openFile 

--launcher.appendVmargs 

--vm 

C:\Program Files\Java\jdk1.8.0_211\bin\javaw.exe

 

블로그 이미지

uchacha

개발자 일지

,

방법1.

@ 참고 : https://webnautes.tistory.com/1158

▶ #1, #2, #5는 참고2 활용

#1. C/C++ 컴파일러(MinGW) 설치
#2. 'Code Runner' 확장 기능 설치
#3. 메뉴 > 터미널 > 기본 빌드 작업 구성 -> 탐색기에 tasks.json파일이 추가됨
#4. tasks.json의 내용을 다음으로 교체

{
    "version": "2.0.0",
    "runner": "terminal",
    "type": "shell",
    "echoCommand": true,
    "presentation" : { "reveal": "always" },
    "tasks": [
          //C++ 컴파일
          {
            "label": "save and compile for C++",
            "command": "g++",
            "args": [
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}"
            ],
            "group": "build",

            //컴파일시 에러를 편집기에 반영
            //참고:   https://code.visualstudio.com/docs/editor/tasks#_defining-a-problem-matcher

            "problemMatcher": {
                "fileLocation": [
                    "relative",
                    "${workspaceRoot}"
                ],
                "pattern": {
                    // The regular expression. 
                   //Example to match: helloWorld.c:5:3: warning: implicit declaration of function 'prinft'
                    "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning error):\\s+(.*)$",
                    "file": 1,
                    "line": 2,
                    "column": 3,
                    "severity": 4,
                    "message": 5
                }
            }
        },
        //C 컴파일
        {
            "label": "save and compile for C",
            "command": "gcc",
            "args": [
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}"
            ],
            "group": "build",

            //컴파일시 에러를 편집기에 반영
            //참고:   https://code.visualstudio.com/docs/editor/tasks#_defining-a-problem-matcher

            "problemMatcher": {
                "fileLocation": [
                    "relative",
                    "${workspaceRoot}"
                ],
                "pattern": {
                    // The regular expression. 
                   //Example to match: helloWorld.c:5:3: warning: implicit declaration of function 'prinft'
                    "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning error):\\s+(.*)$",
                    "file": 1,
                    "line": 2,
                    "column": 3,
                    "severity": 4,
                    "message": 5
                }
            }
        },
        // 바이너리 실행(Ubuntu)
        // {
        //     "label": "execute",
        //     "command": "cd ${fileDirname} && ./${fileBasenameNoExtension}",
        //     "group": "test"
        // }
        // 바이너리 실행(Windows)
        {
            "label": "execute",
            "command": "cmd",
            "group": "test",
            "args": [
                "/C", "${fileDirname}\\${fileBasenameNoExtension}"
            ]
    
        }
    ]
}

** 이때 단축키 설정해서 사용하면 window has crashed 에러 메세지 뜨므로 단축키 없이 실행!!

#5. 파일 실행(단축키: Ctrl + Alt + N)


방법2. 실행했을 때 output의 encoding이 깨짐

@ 참고 : https://taking.kr/blog/archives/4825.html

#1. MinGW 다운로드

1. https://sourceforge.net/projects/mingw/files/latest/download 다운로드를 통해 설치를 진행한다.
2. 설치 후, MinGW Installation Manager에 다음 패키지를 선택 후, Apply Changes를 눌러 설치한다.

- mingw-developer-toolkit

- mingw32-base

- mingw32-gcc-g++

-msys-base

3. 컴퓨터 -> 속성 -> 고급 시스템 설정 -> 시스템 속성[고급 탭] -> 환경 변수 -> Path 변수에
C:\MinGW\bin을 추가한다.
4. 정상적으로 gcc / g++ 컴파일러가 설치되었는지 확인하기 위해 명령 프롬프트(cmd)를 켜 확인해본다.

- gcc --version

- g++ --version

#2. VS Code 컴파일 환경설정

1. 확장 마켓플레이스에서 'Code Runner'를 설치한다.
- 지원 언어 : C, C++, Java, Python, Visual Basic .Net, Kotlin 등
2. 설정(ctrl+,) -> {} settings.json 안에 다음 설정 추가

   // Code Runner "CPP" 설정

    "code-runner.executorMap": {

    "cpp": "cd $dir && g++ $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt"

    },

    // Window 10 에서는 Default Shell 이 Powershell이기 때문에 cmd로 변경해야 정상적으로 실행 됨.

    "terminal.integrated.shell.windows": "C:\\Windows\\sysnative\\cmd.exe"

3. 파일 생성 후 실행(단축키: Ctrl+Alt+N)

- helloworld.c

#include <stdio.h>

int main() {
    printf("안녕\n");
    return 0;
}

- helloworld.cpp

#include <iostream>
using namespace std;

int main() {
    cout << "안녕" << endl;
    return 0;
}

'프로그램 설치' 카테고리의 다른 글

이클립스 CDT로 C/C++ 개발 시작하기  (0) 2019.09.05
블로그 이미지

uchacha

개발자 일지

,