도커 파일을 빌드할 때 패키지 관리자와 interative하게 제어할 수 없다..



yum을 사용할 때는 어떠한 interative 입력을 받지 않도록 -y를 사용하는 반면,


apt-get은 interactive 입력을 받지 않도록 다음과 같이 사용해야 한다. 

DEBIAN_FRONTEND=noninteractive




예)

FROM ubuntu:18.04

RUN apt-get update -q \

&& DEBIAN_FRONTEND=noninteractive apt-get install -qy mysql-client


Posted by '김용환'
,



css style에 background-image를 추가하는 것이 있다. 


background-image:url(https://barcode.google.com/123123);background-repeat:no-repeat;background-position: center




도움 되는 아티클

https://www.w3schools.com/cssref/pr_background-position.asp


https://www.codingfactory.net/10571


https://www.w3schools.com/cssref/pr_background-repeat.asp

Posted by '김용환'
,


예)


ENV WIDTH 360 ...

CMD ["sh", "-c", "phantomjs --ssl-protocol=any rasterize.js 
      3001 $WIDTH"]


docker run -e WIDTH=360 -p 3001:3001 phantomjs


CMD 안에서는 제약사항이 많으니 sh를 잘 사용하는 것이 좋다. 








Posted by '김용환'
,


카카오 무료 폰트


https://brand.kakaocorp.com/assets/index/font


네이버 나눔 폰트


https://hangeul.naver.com/2017/nanum


나눔 폰트 재배포/판매시 라이선 전문을 포함하거나 출처 표기를 권장한다고 한다. 

 https://help.naver.com/support/contents/contents.help?serviceNo=1074&categoryNo=3497


네이버 나눔글꼴은 본 저작권 안내와 라이선스 전문을 포함해서 

다른 소프트웨어와 번들하거나 재배포 또는 판매가 가능합니다. 

 

네이버 나눔글꼴 라이선스 전문을 포함하기 어려울 경우, 

나눔글꼴의 출처 표기를 권장합니다. 



우아한 폰트

https://www.woowahan.com/#/fonts



Posted by '김용환'
,


phantomjs을 dockernize할 때 아래와 같이 fontconfig, freetype을 설치한다해도

RUN yum install -y fontconfig fontconfig-devel freetype freetype-devel libstdc++ ; yum clean all



영문이 깨지는 경우가 있다. 이 때는 다음 패키지를 설치해야 영문이 제대로 출력된다. 

urw-fonts

Posted by '김용환'
,



https://github.com/knight76/springboot2-restdocs  참고


Spring Rest Docs 및 Junit Test를 진행할 때 아래와 같이 부모 테스트 클래스를 둔 후, 자식 테스트에서 로직만 테스트하게 했더니 좀 편했다.


부모 테스트 클래스 

package com.example.controller;

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;

import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

public class ParentTest {

@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();

@Autowired
WebApplicationContext context;

RestDocumentationResultHandler document;

MockMvc mockMvc;

@Before
public void setUp() {
this.document = document(
"{class-name}/{method-name}",
preprocessResponse(prettyPrint())
);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration( this.restDocumentation))
.alwaysDo(document)
.build();
}
}





자식 테스트 클래스 

package com.example.controller;

import static org.hamcrest.CoreMatchers.*;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.restdocs.request.RequestDocumentation.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.ResultActions;

@RunWith(SpringRunner.class)
@AutoConfigureRestDocs
@SpringBootTest
public class HelloWorldTest extends ParentTest {

@Test
public void helloWorldJson() throws Exception {
// given
String response = "{\"result\":\"Welcome, Hello World, Samuel\"}";

// when
ResultActions result =
mockMvc.perform(get("/helloworld/json")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.param("name", "Samuel"))
.andDo(print());

// then
result.andExpect(status().isOk())
.andDo(document.document(
requestParameters(
parameterWithName("name").description("이름입니다.")),
responseFields(
fieldWithPath("result").description("결과입니다."))))
.andExpect(jsonPath("result", is(notNullValue())))
.andExpect(content().string(response));
}
}


Posted by '김용환'
,


Spring Test를 수행하다 엄청 삽질한 것 중의 하나가...


Spring Boot에서 Controller(URL) 테스트를 진행할 때, 

Boot를 실행하고 curl json의 형태로 호출하면 제대로 호출되면 동작하는데, 

MockMvc를 사용하면 동작이 안되는 경우가 있었다.



Boot서비스가 실행되는 것과 달리 Spring Test와 MockMvc를 이용할 때는 굉장히 세밀하게 확인한다. 

그래서 JSON을 사용할 때는 반드시 MediaType을 적용하는 게 삽질을 덜하게 한다. 


@RestController
@RequestMapping(path="/api/1",
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class CitiesController {


Posted by '김용환'
,


nodejs + chrome headless + puppeteer로 캡쳐하는 앱을 테스트해봤다.

https://github.com/knight76/chrome-headless-puppeteer





chrome headless 단독으로 사용하기 어렵고 nodejs의 puppeteer(https://github.com/GoogleChrome/puppeteer)을 같이 써야 한다.


server.js

const http = require('http')
const puppeteer = require('puppeteer')
const url = require('url')

const DEFAULT_WIDTH = 800
const DEFAULT_HEIGHT = 600
const port = 3000

http.createServer(async (request, response) => {
const urlParts = url.parse(request.url, true)
const urlPathname = urlParts.pathname
const urlParams = urlParts.query
const requrl = urlParams['url']

if (!requrl) {
response.writeHead(200)
response.end('Capture Service.')
return
}

let browerWidth, browerHeight
if (urlPathname === '/capture') {
if (parseInt(urlParams['w'], 0) > 0) {
browerWidth = parseInt(urlParams['w'], 0)
}
if (parseInt(urlParams['h'], 0) > 0) {
browerHeight = parseInt(urlParams['h'], 0)
}
} else if (urlPathname === '/check.html') {
response.writeHead(200)
response.end()
} else {
response.writeHead(500)
response.end()
return
}

if (!browerWidth) {
browerWidth = DEFAULT_WIDTH
}
if (!browerHeight) {
browerHeight = DEFAULT_HEIGHT
}

let page
try {
const browser = await puppeteer.launch({
args: ['--no-sandbox']
});

page = await browser.newPage()
await page.setViewport({
width: browerWidth,
height: browerHeight
})

await page.goto(requrl, {
waitUntil: 'networkidle2'
})

const screenshot = await page.screenshot({
type: 'jpeg',
quality: 100
})

console.log("5")
response.writeHead(200, {
'content-type': 'image/jpg',
'cache-control': 'public,max-age=60,immutable'
})

response.end(screenshot, 'binary')
} catch (e) {
console.log(e)
response.writeHead(500, {
'Content-Type': 'text/plain'
})
response.end('UNKNOWN ERROR(500)!!')
} finally {
if (page && page.isClosed() == false) {
await page.close()
}
}
}).listen(port)

console.log(`Capture server running port : ${port}...`)



Dockerfile

FROM zenato/puppeteer
USER root
COPY . /usr/src/app
RUN cd /usr/src/app && npm install
EXPOSE 3000
WORKDIR /usr/src/app
CMD [ "node", "server.js" ]




참고로 파이썬 + cProto(https://pypi.org/project/cproto/)를 사용하려 했으나 파이썬 쪽으로는 이제 scraping은 좀 되는데, 화면 캡쳐는 괜찮은 라이브러리가 없다. 


chrome headless는 nodejs와 잘 맞는 것 같다. 






Posted by '김용환'
,

 

phantomjs에는 viewport 조절 기능이 있지만,

기본 chrome headless에서 없다. 따라서 아래와 같이 nodejs를 개발해야 한다.


https://medium.com/@dschnr/using-headless-chrome-as-an-automated-screenshot-tool-4b07dffba79a


참고로 python쪽을 찾아보려 했지만.. 없다.

(chrome의 remote api인 cproto 라는 게 있지만 더 이상 개발되지 않아서 쓸모가 없다. )


nodejs로 개발해야 할 듯하다. 



Posted by '김용환'
,


Java/Spring에서 이미지를 다운로드하는 예시이다.

byte[]로 다운받고 java.nio.file.Files, Paths를 사용해 다운로드받는다. 


@Service
public class Downloader {

@Autowired
private RestTemplate restTemplate;

public void download(String url) throws Exception {

byte[] binary = restTemplate.getForObject(url, byte[].class);
String fileformat = String.format("image.jpg");
Files.write(Paths.get(fileformat), binary);
}

}


Posted by '김용환'
,