BaseTypeHandler를 확장하여  사용자 정의 테이블에 대응되는 객체를 만들어 줍니다.
 
Handler
public class TypeUpDataVibHandler extends BaseTypeHandler<List<TypeUpDataVibVo>>{ 
	  @Override 
	  public void setNonNullParameter(PreparedStatement ps, 
	      int i, List<TypeUpDataVibVo> parameter, JdbcType jdbcType) 
	      throws SQLException { 
	    SQLServerDataTable tvp = new SQLServerDataTable(); 

        tvp.addColumnMetadata("C1" ,java.sql.Types.NVARCHAR); 
        tvp.addColumnMetadata("C2" ,java.sql.Types.NVARCHAR); 
        tvp.addColumnMetadata("C3" ,java.sql.Types.NVARCHAR); 
        tvp.addColumnMetadata("C4" ,java.sql.Types.NVARCHAR); 
        tvp.addColumnMetadata("C5" ,java.sql.Types.NVARCHAR); 
        tvp.addColumnMetadata("C6" ,java.sql.Types.NVARCHAR); 

	    for (TypeUpDataVibVo param : parameter) { 
	    	tvp.addRow( 

	    			param.getC1(), 
	    			param.getC2(), 
	    			param.getC3(), 
	    			param.getC4(), 
	    			param.getC5(), 
	    			param.getC6()
				); 
	    } 
	    ps.unwrap(SQLServerPreparedStatement.class).setStructured(i, "TYPE_UP_DATA_VIB_BULK", tvp); 
	  } 
	  @Override 
	  public List<TypeUpDataVibVo> getNullableResult(ResultSet rs, String columnName) throws SQLException { 
	    return null; 
	  } 
	  @Override 
	  public List<TypeUpDataVibVo> getNullableResult(ResultSet rs, int columnIndex) throws SQLException { 
	    return null; 
	  } 
	  @Override 
	  public List<TypeUpDataVibVo> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { 
	    return null; 
	  } 
}
 

VO

public class TypeUpDataVibVo { 
	String C1; 
	String C2; 
	String C3; 
	String C4; 
	String C5; 
	String C6; 


	public String getC1() { 
		return C1; 
	} 
	public void setC1(String c1) { 
		C1 = c1; 
	} 
	public String getC2() { 
		return C2; 
	} 
	public void setC2(String c2) { 
		C2 = c2; 
	} 
	public String getC3() { 
		return C3; 
	} 
	public void setC3(String c3) { 
		C3 = c3; 
	} 
	public String getC4() { 
		return C4; 
	} 
	public void setC4(String c4) { 
		C4 = c4; 
	} 
	public String getC5() { 
		return C5; 
	} 
	public void setC5(String c5) { 
		C5 = c5; 
	} 
	public String getC6() { 
		return C6; 
	} 
	public void setC6(String c6) { 
		C6 = c6; 
	} 
}

 

xml 

<select id="saveUpload_bulk" statementType="CALLABLE" resultType="hashMap"> 
{ 
    call P_BULK(#{list, typeHandler=kr.common.data.TypeUpDataVibHandler}) 
} 
</select>
 
service 
@Override 
public void saveUpload_bulk(List<TypeUpDataVibVo> list) throws SQLServerException { 
    tsMapper.saveUpload_bulk(list); 
}

테이블 반환 매개 변수 사용

 

멀티 로우를 데이터 삽입시에 SQLServerDataTable 를 사용하여 한번에 table parameter로 넘기는 방법 넘기는 방법

 

pom.xml

<dependency> 
    <groupId>com.microsoft.sqlserver</groupId> 
    <artifactId>mssql-jdbc</artifactId> 
    <version>9.2.1.jre8</version> 
</dependency>

 

controller
List<HashMap<String, Object>> mapList = new ArrayList<>(); 
for( int i = 0; i < list1.size(); i++ ) { 
    HashMap<String, Object> paramMap = (HashMap<String, Object>) params.clone(); 
    String key = list1.get(i).get(0); 
    paramMap.put("x1", list1.get(i).get(1)); 
    paramMap.put("x2", list1.get(i).get(3)); 
    paramMap.put("y1", list2.get(i).get(key).getField1()); 
    paramMap.put("y2", list2.get(i).get(key).getField2()); 
    paramMap.put("z1", list3.get(i).get(key).getField1()); 
    paramMap.put("z2", list3.get(i).get(key).getField2()); 
    mapList.add(paramMap); 
}				 
tsServiceImpl.saveQma003Upload_bulk(mapList);
 
service
@Autowired 
private DataSource dataSource;
table parameter에 row를 add하여 프로시저 호출

 

public boolean saveUpload_bulk(List<HashMap<String, Object>> mapList) throws SQLServerException { 
	        SQLServerDataTable tvp = new SQLServerDataTable(); 
	        // Define metadata for the data table.   
	        
	        tvp.addColumnMetadata("C1" ,java.sql.Types.NVARCHAR); 
	        tvp.addColumnMetadata("C2" ,java.sql.Types.NVARCHAR); 
	        tvp.addColumnMetadata("C3" ,java.sql.Types.NVARCHAR); 
	        tvp.addColumnMetadata("C4" ,java.sql.Types.NVARCHAR); 
	        tvp.addColumnMetadata("C5" ,java.sql.Types.NVARCHAR); 
	        tvp.addColumnMetadata("C6" ,java.sql.Types.NVARCHAR); 
	         
	        for (HashMap<String, Object> params : mapList) { 
	        	 tvp.addRow( 
	        				StringUtil.blankIfNull(params.get("x1")),//c1 
	        				StringUtil.blankIfNull(params.get("x2")),//c2 
	        				StringUtil.blankIfNull(params.get("y1")),//c3 
	        				StringUtil.blankIfNull(params.get("y2")),//c4 
	        				StringUtil.blankIfNull(params.get("z1")),//c5 
	        				StringUtil.blankIfNull(params.get("z2")),//c6 
	                ); 
			}			 
	         
	        String SQL_COMMAND = "{call P_BULK (?)}"; 
	         
	        try  { 
	        	CallableStatement  callableStmt  = dataSource.getConnection().prepareCall(SQL_COMMAND); 
	        	SQLServerPreparedStatement sqlServerStatement = callableStmt.unwrap(SQLServerPreparedStatement.class); 
	        			 
	        	sqlServerStatement.setStructured(1, "TYPE_UP_DATA_VIB_BULK", tvp); 
	        	return sqlServerStatement.execute(); 
	        } catch (SQLTimeoutException e) { 
				// TODO Auto-generated catch block 
				e.printStackTrace(); 
			} catch (SQLException e1) { 
				// TODO Auto-generated catch block 
				e1.printStackTrace(); 
			} 
	         
	        return false; 
	    }
 
MSSQL
테이블 반환 매개 변수 형식
CREATE TYPE [dbo].[TYPE_UP_DATA_VIB_BULK] AS TABLE( 
	[C1] [nvarchar](50) NULL, 
	[C2] [nvarchar](50) NULL, 
	[C3] [nvarchar](50) NULL, 
	[C4] [nvarchar](50) NULL, 
	[C5] [nvarchar](50) NULL, 
	[C6] [nvarchar](50) NULL
)
 
프로시저
ALTER PROCEDURE [dbo].[P_BULK]  
( 
    @itblParam TYPE_UP_DATA_VIB_BULK Readonly, 
	@p_error_code       VARCHAR(100) = ''   OUTPUT,     -- 사용자 에러코드 리턴   
    @p_row_count        INT = 0             OUTPUT,     -- 실행/리턴하는 레코드행수  
    @p_error_note       NVARCHAR(100) = ''  OUTPUT,     -- 사용자 지정 문자열   
    @p_return_str       NVARCHAR(100) = '' OUTPUT,     -- 사용자 지정 반환값   
    @p_error_str        NVARCHAR(1000) = '' OUTPUT,     -- 오류메세지  
    @ErrorState         VARCHAR(500) = ''   OUTPUT,     -- 오류번호/심각도/오류상태번호오류발생루틴내의 줄번호 
    @ErrorProcedure     NVARCHAR(200) = ''  OUTPUT      -- 오류발생 프로시저/트리거   
)  
AS  
SET NOCOUNT ON   
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 
--select * from UP_DATA_VIB_VURK; 
BEGIN  
    BEGIN TRY 
       -----------------------------------------------------------------------------------------------------------------------  
        -- 첫번째 리턴 레코드셋 처리  --첫 번째 Excel Insert 
		INSERT INTO UP_DATA_VIB_VURK  
		(C1, C2, C3, C4, C5, C6) 
		SELECT  
		C1, C2, C3, C4, C5, C6
		FROM @itblParam AS nr; 
		 SELECT @p_row_count = @@ROWCOUNT 
        -- 에러가 없을 경우에 정상적인 처리 진행 
        IF @p_row_count > 0 
            SET @p_error_code = 'MSG0002'                                   -- 정상적으로 등록되었습니다. 
        ELSE 
            SET @p_error_code = 'MSG0029'                                   -- 등록된 레코드가 없습니다.   
        -----------------------------------------------------------------------------------------------------------------------  
    END TRY 
    BEGIN CATCH 
        SELECT @p_error_code =  CASE SUBSTRING('N',1,1) 
                                  WHEN 'Q' THEN 'ERR0006'                 -- 조회시 에러가 발생하였습니다.  
                                  WHEN 'N' THEN 'ERR0008'                 -- 등록시 오류가 발생하였습니다. 
                                  WHEN 'U' THEN 'ERR0009'                 -- 수정시 오류가 발생하였습니다. 
                                  WHEN 'D' THEN 'ERR0010'                 -- 삭제시 오류가 발생하였습니다. 
                                           ELSE 'ERR0000' 
                                  END 
        SELECT @ErrorState = CONVERT(VARCHAR(100), ERROR_NUMBER()) + '|' + CONVERT(VARCHAR(100), ERROR_SEVERITY()) + '|' + CONVERT(VARCHAR(100), ERROR_STATE()) + '|' + CONVERT(VARCHAR(100), ERROR_LINE()), 
                @ErrorProcedure = 'PRC=' + ERROR_PROCEDURE(), @p_error_str = ERROR_MESSAGE(); 
    END CATCH; 
END
 

개요 

Selenium을 이용한 웹 크롤러

GUI 환경에서 설정하여 순차적 실행 자동화 프로그램 

기본 기능 

1. URL이동 

2. INPUT에 지정한 값을 입력 

3. 버튼 클릭 

4. FRAME 이동 

5. 글 목록 가져오기  

6. 가져온 글 저장하기 

설정한 값들을 순차적 실행.

 

프로그램 다운로드 

1. GITHUB에 전체 소스 및 실행파일이 올라가 있으며 하기 URL 다운로드

 

 

세부설명

 

1.  그룹 리스트

  - 작업 리스트의 묶음을 관리하기 위한 그룹 리스트

 
2. 작업 리스트

  - 하나의 작업을 지정할 할 수 있습니다.

  - 컬럼정보
 
이름 작업이름
Target구분
1. single :하나의 element에 대한 수행
2. multiple : 부모 element에 대한 하위 element를 수행할때
    multiple이고 target정보가 동일한 것을 하나의 작업 그룹으로
    지정됨
3. url : 이동
4. frame : frame이동
Target정보 Target input
액션_구분 1. click
2. get
3. set
선택자구분 1. xpath
2. id
3. class
4. query_selector
선택자 선택자 input
Input 액션 set인 경우 input입력 
확인 선택 행 실행 
위로 행 위로 이동
아래로 행 아래로 이동

3. 실행 결과 : 실행 시 단위 이름 결과가 출력

4. 그룹 실행 결과 : multiple의 경우 하위 element를 테이블 형태로 출력됨.

 

네이버 카페 글 목록 가져오기 설정

1. 그룹 추가 

2. url 이동 

3. 카페 메뉴 클릭 

 

4. 프레임 이동

 

5. 글 가져오기 설정 

 

6. 실행 > 글가져오기

 

 

이슈 

- pandas 모듈로 엑셀파일 로드시 하기 에러발생시

import pandas as pd

raw_data = pd.read_excel('./data/teenage_mental.xls')
ImportError                               Traceback (most recent call last)
<ipython-input-4-7fe267b63e94> in <module>()
      1 import pandas as pd
      2 
----> 3 raw_data = pd.read_excel('./data/teenage_mental.xls')

4 frames
/usr/local/lib/python3.7/dist-packages/pandas/compat/_optional.py in import_optional_dependency(name, extra, errors, min_version)
    139                 return None
    140             elif errors == "raise":
--> 141                 raise ImportError(msg)
    142 
    143     return module

ImportError: Pandas requires version '1.2.0' or newer of 'xlrd' (version '1.1.0' currently installed).

---------------------------------------------------------------------------
NOTE: If your import is failing due to a missing package, you can
manually install dependencies using either !pip or !apt.

To view examples of installing some common dependencies, click the
"Open Examples" button below.
---------------------------------------------------------------------------

 

방법 

하기 명령 실행후 runtime 재시작 

!pip install --upgrade xlrd

 

'머신러닝' 카테고리의 다른 글

colab 그래프에서 한글설정  (0) 2022.05.11

Colab에서 한글이 깨져보이는 경우 

 

!apt-get update -qq
!apt-get install fonts-nanum* -qq
import matplotlib.pyplot as plt
%matplotlib inline 

import matplotlib as mpl

plt.rcParams['axes.unicode_minus'] = False 
path = '/usr/share/fonts/truetype/nanum/NanumGothicEco.ttf'
font_name = mpl.font_manager.FontProperties(fname=path).get_name()

mpl.rc('font', family=font_name)
mpl.font_manager._rebuild()
plt.plot([0,1], [0,1])
plt.title('한글 테스트')
plt.show()

 

'머신러닝' 카테고리의 다른 글

colab xlrd오류 대응  (0) 2022.05.11

https://github.com/ReactiveX/RxSwift

 

ReactiveX/RxSwift

Reactive Programming in Swift. Contribute to ReactiveX/RxSwift development by creating an account on GitHub.

github.com

 

RxSwift는 Swfit로 작성된 반응형 익스텐션 버전입니다.

Observable을 만들어 스트림에서 값 및 기타 이벤트를 구독하여 로직을 분리시킬 수 있고

비동기 및 함수적 스타일 연산자를 사용하여 를 사용하여 데이터를 참조하는 방법을 제공합니다.

 

1. pod 설정

pod 'RxSwift', '6.0.0-rc.2'
pod 'RxCocoa', '6.0.0-rc.2'

 

2. Observable 만들기 

static func fetchLotto(_ order: Int) -> Observable<Lotto?> {
        return Observable.create() { emitter in
            let url = URL(string: domainUrlString + "?order=" + String(order))!
            let task = session.dataTask(with: url, completionHandler: { (data: Data?, response: URLResponse?, err: Error?) -> Void in
                guard err == nil else {
                    emitter.onError(err!)
                    return
                }
                
                let lottoDto = try? JSONDecoder().decode(Lotto.self, from: data!)
                emitter.onNext(lottoDto)
            })
            
            task.resume()
            
            return Disposables.create(){
                task.cancel()
            }
        }
    }

 

3. subscribe 사용하기 

let observable = APIService.fetchLotto(order)
        _ = observable
            .observe(on: MainScheduler.instance)
            .subscribe(onNext: {
                self.lotto = $0
                self.viewUpdate()
            })
            .disposed(by: disposeBag)

 

옵저버블은 3가지 이벤트를 방출합니다.

public enum Event<Element> {
  case next(Element) //정상
  case error(Swfit.Error) //에러
  case completed //완료 
}

 

4. 마무리

위와 같이 행위를 Api를 Observable을 만들고 사용하는 쪽에서 Observable을 return받아 사용할 수 있습니다.

이와 같은 장점은 디자인 아키텍쳐 측면에서 비즈니스 로직을 분리하고 테스트를 별도로 할 수 있고

비동기 행위들을 Observable을 만들어 사용하는 쪽에서 일과된 코드를 유지할 수 있으며 

MVVM구조와 같은 구조에서 화면에서는 ViewModel 데이터에만 집중할 수 있는 장점으로 활용할 수 있습니다. 

 

SSL이란

SSL(Secure Sockets Layer)은 암호화 기반 인터넷 보안 프로토콜입니다. 인터넷 통신의 개인정보 보호, 인증, 데이터 무결성을 보장하기 위해 Netscape가 1995년 처음으로 개발했습니다. SSL은 현재 사용 중인 TLS 암호화의 전신입니다.

 

TrustKit이란

TrustKit은 모든 iOS 10+, macOS 10.10+, tvOS 10+ 또는 watchOS 3+ 앱에서 SSL 공개 키 고정 및보고를 쉽게 배포 할 수있는 오픈 소스 프레임 워크입니다. Swift 및 Objective-C 앱을 모두 지원합니다.

수동으로 고정을 구성하는 것은 어렵고 시간이 많이 소요될 수 있습니다. TrusKit 은 코드 몇 줄만 작성 하여 인증서의 공개 키 를 확인 하는 매우 쉬운 방법 을 제공하고 추가 기능도 제공합니다.

https://github.com/datatheorem/TrustKit

https://github.com/datatheorem/TrustKit/blob/master/docs/getting-started.md

 

적용하기

1.인증서 다운로드

브라우저에서 사이트 인증서 다운로드 

 

2.인증서로부터 pin추출

https://github.com/datatheorem/TrustKit/blob/master/get_pin_from_certificate.py

python 소스 다운로드 후

 

Pin 추출 : U3xTWXJOd447ON2zOz9w35qNaPAJDpqlFO4Jt/443us= 

$ python3 get_pin_from_certificate.py --type DER \*.google.com.cer
CERTIFICATE INFO
----------------
b'subject= /C=US/ST=California/L=Mountain View/O=Google LLC/CN=*.google.com\nissuer= /C=US/O=Google Trust Services/CN=GTS CA 1O1\nSHA1 Fingerprint=E4:89:43:D9:6A:40:D5:34:B9:33:7E:E5:ED:A9:76:D2:20:1D:2E:BF\n'

TRUSTKIT CONFIGURATION
----------------------
kTSKPublicKeyHashes: @[@"b'U3xTWXJOd447ON2zOz9w35qNaPAJDpqlFO4Jt/443us='"] // You will also need to configure a backup pin

 

3.프로젝트에 TrustKit적용

pod 'TrustKit'
$ pod install

 

AppDelegate.swift

도메인 설정과 추출한 정보를 kTSKPublicKeyHashes값으로 설정

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        TrustKit.setLoggerBlock { (message) in
              print("TrustKit log: \(message)")
        }
        let trustKitConfig: [String: Any] = [
             kTSKSwizzleNetworkDelegates: false,
             kTSKPinnedDomains: [
                    "www.google.com": [
                           kTSKEnforcePinning: false,
                           kTSKIncludeSubdomains: true,
                           kTSKPublicKeyHashes: [
        //First public key -> Obtained from the Python script
        "U3xTWXJOd447ON2zOz9w35qNaPAJDpqlFO4Jt/443us=",
        //Second public key in case of the first one will expire
        "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="
           ],
           kTSKReportUris:        ["https://overmind.datatheorem.com/trustkit/report"],
         ]
        ]]
        TrustKit.initSharedInstance(withConfiguration: trustKitConfig)
        
        return true
    }

 

HomeViewController.swift

 

delegate: self를 통해 재정의한 urlSession이 호출 되도록 선언함 

lazy var session: URLSession = {
       URLSession(configuration: URLSessionConfiguration.ephemeral,
                  delegate: self,
                  delegateQueue: OperationQueue.main)
    }()
let task = session.dataTask(with: url, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in
	guard let data = data else {
		completion(nil)
		return
	}
	let str = String(decoding: data, as: UTF8.self)
	print(str)
})
task.resume()
extension HomeViewController: URLSessionDelegate {
    
    func urlSession(_ session: URLSession,
                    didReceive challenge: URLAuthenticationChallenge,
                    completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
                                                
        if TrustKit.sharedInstance().pinningValidator.handle(challenge, completionHandler: completionHandler) == false {
            // TrustKit did not handle this challenge: perhaps it was not for server trust
            // or the domain was not pinned. Fall back to the default behavior
            completionHandler(.performDefaultHandling, nil)
        }
    }
}

URLSessionDelegate을 상속받아 urlSession에서 재정의하여 TrustKit을 통해 검증이 되도록 합니다. 

위와 같이 적용하여 정상적으로 동작하는 것을 확인합니다. 

서버에 https로 인증서를 적용하게되면 안드로이드에서 에러가 발생하게 됩니다.

   javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
            at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374)
            at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209)
            at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
            at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
            at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
            at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
            at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
            at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
            at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
    

 

정상적으로 접속하기 위해서 발급받은 인증서를 적용하도록 합니다.

자세한 내용은 https://developer.android.com/training/articles/security-ssl?hl=ko#kotlin 입니다.

 

코드 흐름은

  1. 인증서를 로드 (LetEncryption에서 fullchain.pem을 사용)

  2. 신뢰할 수있는 CA를 포함하는 키 스토어 생성

  3. CA 입력을 신뢰하는 TrustManager 생성

  4. TrustManager를 사용하는 SSLContext 생성

  5. OkHttpcliennt 적용

 

으로 전체 소스는 하기와 같습니다.

companion object {
        private const val BASE_URL = SERVER_API
        var VERIFY_DOMAIN: String = "*.lottois.info"

        fun create(context: Context): ApiService {
            val logger =
                HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }

            val tmf: TrustManagerFactory? = getTrustManagerFactory(context)
            var sslsocket = tmf?.let { getSSLSocketFactory(it) }
            val hostnameVerifier = HostnameVerifier { _, session ->
                HttpsURLConnection.getDefaultHostnameVerifier().run {
                    verify(VERIFY_DOMAIN, session)
                }
            }

            val client = OkHttpClient.Builder()
                .addInterceptor(logger)
                .sslSocketFactory(sslsocket, tmf?.trustManagers?.get(0) as X509TrustManager)
                .hostnameVerifier(hostnameVerifier)
                .build()

            return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(ApiService::class.java)
        }

        private fun getTrustManagerFactory(context: Context): TrustManagerFactory? {
            // 1. CA 로드 
            val cf: CertificateFactory = CertificateFactory.getInstance("X.509")
            val caInput: InputStream = context.resources.openRawResource(R.raw.fullchain)
            val ca: X509Certificate = caInput.use {
                cf.generateCertificate(it) as X509Certificate
            }

            // 2. 신뢰할 수있는 CA를 포함하는 키 스토어 생성
            val keyStoreType = KeyStore.getDefaultType()
            val keyStore = KeyStore.getInstance(keyStoreType).apply {
                load(null, null)
                setCertificateEntry("ca", ca)
            }

            // 3. CA 입력을 신뢰하는 TrustManager 생성
            val tmfAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm()
            val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm).apply {
                init(keyStore)
            }
            return tmf
        }

        private fun getSSLSocketFactory(
            tmf: TrustManagerFactory
        ): SSLSocketFactory? {
            //4. TrustManager를 사용하는 SSLContext 생성
            val sslContext: SSLContext = SSLContext.getInstance("TLS")
            sslContext.init(null, tmf.trustManagers, null)
            return sslContext.socketFactory
        }
    }

 

https://certbot.eff.org/docs/index.html

docker를 활용해서 certbot으로 무료 인증서를 발급아서 적용해 보도록 하겠습니다.

certbot 의 manual 기능을 이용하여 서버외 다른 pc에서 인증서를 발급받기 위하여

dns서버에서 txt값으로 검증통해 발급을 받도록 합니다.

 

1.docker로 실행

- 인증서가 저장될 host 경로를 volume으로 설정

- domain을 입력합니다.

docker run -it --rm --name certbot   -v '[host_path]:/etc/letsencrypt'   -v '

[host_path]:/var/lib/letsencrypt'   certbot/certbot certonly -d '[domain]' --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory

 

2 설정화면

1. email설정

Saving debug log to /var/log/letsencrypt/letsencrypt.log

Plugins selected: Authenticator manual, Installer None

Enter email address (used for urgent renewal and security notices)

(Enter 'c' to cancel): [email]

 

2.동의

Please read the Terms of Service at

https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must

agree in order to register with the ACME server. Do you agree?

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(Y)es/(N)o: Y

 

3.동의

Would you be willing, once your first certificate is successfully issued, to

share your email address with the Electronic Frontier Foundation, a founding

partner of the Let's Encrypt project and the non-profit organization that

develops Certbot? We'd like to send you email about our work encrypting the web,

EFF news, campaigns, and ways to support digital freedom.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(Y)es/(N)o: Y

 

4.dns txt 설정 hash값 확인

Please deploy a DNS TXT record under the name

_acme-challenge.lottois.info with the following value:



qjbAHdtj.....



Before continuing, verify the record is deployed.

 

5.4번에서 value값을 dns 서버 TXT값으로 입력을 합니다.

6. dig 명령으로 전파가 되었는지 확인해 봅니다.

$ dig -t txt _acme-challenge.[domain]


; <<>> DiG 9.10.6 <<>> -t txt _acme-challenge.[domain]

;; global options: +cmd

;; Got answer:

;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28324

;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1



;; OPT PSEUDOSECTION:

; EDNS: version: 0, flags:; udp: 1232

;; QUESTION SECTION:

;_acme-challenge.[domain]. IN TXT



;; ANSWER SECTION:

_acme-challenge.[domain]. 86400 IN TXT "qjbAHdtj_...."



;; Query time: 93 msec

;; SERVER: 1.1.1.1#53(1.1.1.1)

;; WHEN: Mon Dec 14 13:18:50 KST 2020

;; MSG SIZE rcvd: 113

 

7.확인후 Enter

Press Enter to Continue

Waiting for verification...

Cleaning up challenges

Subscribe to the EFF mailing list (email: [email]).

We were unable to subscribe you the EFF mailing list because your e-mail address appears to be invalid. You can try again later by visiting https://act.eff.org.



IMPORTANT NOTES:

- Congratulations! Your certificate and chain have been saved at:

/etc/letsencrypt/live/lottois.info/fullchain.pem

Your key file has been saved at:

/etc/letsencrypt/live/lottois.info/privkey.pem

Your cert will expire on 2021-03-14. To obtain a new or tweaked

version of this certificate in the future, simply run certbot

again. To non-interactively renew *all* of your certificates, run

"certbot renew"

- If you like Certbot, please consider supporting our work by:



Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate

Donating to EFF: https://eff.org/donate-le

 

8.볼륨 지정 폴더 확인

 

privkey.pem 과 fullchainn.pem가 생성된 것을 확인할 수 있습니다. 

 

3.nginx 적용

위 정보를 nginx에 적용한 설정파일은 하기 fullchain.pem, privkey.pem파일을 활용하여

https로 적용되는것을 확인하였습니다.

http {
    access_log  /var/log/nginx/access.log;
    error_log   /var/log/nginx/error.log;

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    server {
        listen 80;
        server_name lottois.info;
        return 301 https://$host$request_uri;    
    }
    
    server {
        listen 443 ssl;
        server_name lottois.info;
        ssl_certificate /etc/nginx/cert/fullchain.pem;
    	ssl_certificate_key /etc/nginx/cert/privkey.pem;
        
        ssl_session_cache shared:le_nginx_SSL:1m;
	    ssl_session_timeout 1440m;
	
	    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
	    ssl_prefer_server_ciphers on;
	
	    ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS";
    
        location / {
            proxy_pass         http://lottoweb:8080;
        }    
    }
    
    sendfile        on;
    keepalive_timeout  65;
    include /etc/nginx/conf.d/*.conf;
}

 

'DevOps' 카테고리의 다른 글

[Docker] 네트워크, 컨테이너간 통신  (0) 2020.12.12
[Docker] Dockerfile command  (0) 2020.11.20
[Docker] docker 주요 명령어  (0) 2020.11.20
[nginx] nginx 프록시 설정  (0) 2020.11.17

 

AdMob 회원가입 및 프로젝트 생성 

1. admob 가입

https://apps.admob.com/

 

2. 앱 생성
- 앱 추가를 선택

- 안드로이드 선택 

 

3. 광고 단위 추가

 - 저는 배너 광고로 선택하였습니다.

 

프로젝트 적용

1. 프로젝트 수준 build.gradle

allprojects {
    repositories {
        google()
    }
}

 

2. 앱 수준 build.gradle

implementation 'com.google.android.gms:play-services-ads:19.5.0'

 

3. AndroidManifest.xml

앱 > 프로젝트생성 앱 > 앱 설정 앱 ID 하기 입력

하기 value에 가입한 admob에서 프로젝트를 생성한 앱 ID를 입력

<application>
        <!-- Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713 -->
        <meta-data
            android:name="com.google.android.gms.ads.APPLICATION_ID"
            android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy"/>
    </application>

 

4. AdView 추가

노출되는것을 확인하기 위해 하기 adUnitId를 테스트 ID

ca-app-pub-3940256099942544/6300978111 로 진행하도록 합니다.

xml

<com.google.android.gms.ads.AdView
    xmlns:ads="http://schemas.android.com/apk/res-auto"
    android:id="@+id/adView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_gravity="bottom"
    android:layout_marginBottom="5dp"
    ads:adSize="BANNER"
    ads:adUnitId="ca-app-pub-3940256099942544/6300978111">
</com.google.android.gms.ads.AdView>

소스코드

private lateinit var mAdView: AdView
...
fun setupAdMob() {
    MobileAds.initialize(
        this
    ) { }
    mAdView = findViewById(R.id.adView)
    val adRequest = AdRequest.Builder().build()
    mAdView.loadAd(adRequest)
}

 

5. 확인

 - 적용하여 정상적으로 노출하는 것을 확인할 수 있습니다.

 

자세한 내용은 하기 URL에서 확인할 수 있습니다.

https://developers.google.com/admob/android/quick-start?hl=ko

+ Recent posts