토이 프로젝트/Spring&Java 갖고놀기

DataPlay Project - 2 : 이미지 텍스트 번역기 (2) 코드 정리

TerianP 2022. 6. 11.
728x90

DataPlay - 데이터 갖고 놀기 2탄 : 이미지 번역기 코드 정리

이번 글에는 해당 프로젝트의 코드를 정리해보겠습니다

 

1. 파파고 API

- 파파고 API 는 네이버의 파파고 라는 번역기를 사용할 수 있도록 하는 API 이다.

- 당연히 파파고 api 를 사용하기 위한 시크릿키와 아이디값이 있는데 해당 값들은 APIdata 라는 인터페이스 안에 따로 선언해두었다. 

- 파파고 API 를 사용해서 view 에서 들어오는 데이터를 번역 할 수 있도록 한다.

- 네이버 개발자 API 센터에 어떻게 사용하는지 아주 자세하게!! 정말 자세하게 적혀있다. 아래 있는 코드는 해당 코드를 내가 필요한것에 맞게 변형시켜서 만든 코드이다.

- 내가 추가한 코드는 주로 번역될 언어를 선택하는 getSource 메소드, api 로 데이터를 보내는 post 메소드, 번역 결과를 parsing 하는 메소드 등이 있다.

- 코드의 자세한 내용은 주석 참고!

package HJproject.Hellospring.WordTrans;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

public class PapagoAPI {


    public String translator(String word, String source, String target) {
        String clientId = APIdata.naverPaPagoSearchID; // "애플리케이션 클라이언트 아이디값";
        String clientSecret = APIdata.naverPaPagoSecret; // "애플리케이션 클라이언트 시크릿값";

        String apiURL = "https://openapi.naver.com/v1/papago/n2mt"; // 검색 url
        String text;
        try {
            text = URLEncoder.encode(word, "UTF-8"); // 번역할 내용
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("인코딩 실패", e);
        }

        Map<String, String> requestHeaders = new HashMap<>();
        requestHeaders.put("X-Naver-Client-Id", clientId);
        requestHeaders.put("X-Naver-Client-Secret", clientSecret);

        // getSourceAndTarget 메소드를 이용해 API에서 사용가능한 source 언어를 가져온다
        // 정확히는
        String sourceLang = setSource(source);

        // 번역에 사용되는 post 메소드에 apiURL, requestHeader, 프론트 view 에서 넘어온 text 와 해당 text 의 언어
        // 번역할 target 언어를 매개변수로 담아준다
        String responseBody = post(apiURL, requestHeaders, text, sourceLang, target);


        return parsingTrans(responseBody);
        
    }

    // parsingTrans 메소드는 JSON 으로 return 되는 번역 내용을 파싱하기 위한 메소드이다.
    private String parsingTrans(String responseBody){
        // JSON 파싱하는 객체
        JSONParser parser = new JSONParser();
        String transText = null;
        try {
            // response 파싱
            JSONObject parsing = (JSONObject)parser.parse(responseBody);

            // response 파싱한 결과에서 message 에 대한 값을 가져와서 obj 에 저장
            JSONObject obj = (JSONObject) parsing.get("message");
            //System.out.println("obj : "+obj.toString());

            // obj 에서 다시 result 에 대한 값을 가져와서 저장
            JSONObject result = (JSONObject)obj.get("result");
           //System.out.println("result : "+result.toString());

            // result 에서 translatedText 를 가져와서 text 로 저장한 후 return
            transText = result.get("translatedText").toString();
            //System.out.println(text);

        } catch (ParseException e) {
            e.printStackTrace();
        }

        return transText;
    }

    private String post(String apiUrl, Map<String, String> requestHeaders, String text, String source, String target){

        HttpURLConnection con = connect(apiUrl);

        String postParams = "source="+source+"&target="+target+"&text=" + text; // source=원본언어&target=번역언어

        try {
            con.setRequestMethod("POST");
            for(Map.Entry<String, String> header :requestHeaders.entrySet()) {
                con.setRequestProperty(header.getKey(), header.getValue());
            }

            con.setDoOutput(true);
            try (DataOutputStream wr = new DataOutputStream(con.getOutputStream())) {
                wr.write(postParams.getBytes());
                wr.flush();
            }

            int responseCode = con.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) { // 정상 응답
                return readBody(con.getInputStream());
            } else {  // 에러 응답
                return readBody(con.getErrorStream());
            }
        } catch (IOException e) {
            throw new RuntimeException("API 요청과 응답 실패", e);
        } finally {
            con.disconnect();
        }
    }

    private HttpURLConnection connect(String apiUrl){
        try {
            URL url = new URL(apiUrl);
            return (HttpURLConnection)url.openConnection();
        } catch (MalformedURLException e) {
            throw new RuntimeException("API URL이 잘못되었습니다. : " + apiUrl, e);
        } catch (IOException e) {
            throw new RuntimeException("연결이 실패했습니다. : " + apiUrl, e);
        }
    }

    private String readBody(InputStream body){
        InputStreamReader streamReader = new InputStreamReader(body);

        try (BufferedReader lineReader = new BufferedReader(streamReader)) {
            StringBuilder responseBody = new StringBuilder();

            String line;
            while ((line = lineReader.readLine()) != null) {
                responseBody.append(line);
            }

            return responseBody.toString();

        } catch (IOException e) {
            throw new RuntimeException("API 응답을 읽는데 실패했습니다.", e);
        }
    }

    private String setSource(String lang){
        switch (lang) {
            case "kor":
                return "ko";

            case "eng":
                return "en";

            case "jpn":
                return "ja";
        }

        return null;
    }


}

 

2. TransController

- 요청 request에 맞춰 내가 원하는 view 로 보내기 위한 controller.

- 이 부분은 간단한데 하나는 루트 / 로 요청되었을 때와 /transword 로 요청받았을 때 처리하기 위한 내용을 담는다.

package HJproject.Hellospring.Controller;

import HJproject.Hellospring.WordTrans.PapagoAPI;
import lombok.RequiredArgsConstructor;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


@Controller
@RequiredArgsConstructor
public class TransController {
    @GetMapping("/")
    public String home(){
        return "thymeleaf/dataplay/wordtrans";
    }

    @RequestMapping(value = "/transword", method = RequestMethod.GET)
    public void sendData(HttpServletResponse res, HttpServletRequest req) throws IOException {
        // ajax 에서 넘어온 값을 확인 후 변수에 저장
        String text = req.getParameter("text");
        String source = req.getParameter("source");
        String target = req.getParameter("target");
        //System.out.println("text : "+text);

        // 저장된 변수를 PapagoAPI 의 translator 메소드에 넣기 -> 번역된 내용을 trans 변수 안에 저장
        String trans = new PapagoAPI().translator(text, source, target);

        //System.out.println(trans);

        // resp 객체의 문자 타입을 utf-8 로 선언 후 요청 request 가 있었던 web 으로 전달
        res.setContentType("text/plain;charset=utf-8");
        PrintWriter out = res.getWriter();
        out.println(trans);

        //System.out.println(trans);

    }
}

 

3. wordtrans.html

- wordtrans.html 은 프론트에 해당하는 페이지이다.

- 이곳에서 번역이 필요한 사진을 올리고, 해당 사진 안의 내용이 어떤 언어인지 선택한다.

- 사진의 내용의 길이에 따라서 약간의 시간이 필요한데 이때 loading.gif 를 사용해서 내용 추출이 진행되는 듯한 느낌을 받도록 하였다.

- 마지막으로 추출된 내용을 번역하고 싶은 언어를 select 에서 선택한 후 번역하기 버튼을 누르면 바로 번역이 될 수 있도록 만들었다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>wordtranslator</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <!-- tesseract.js CDN -->
    <script src='https://unpkg.com/tesseract.js@2.1.4/dist/tesseract.min.js'></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/tesseract.js/2.1.5/tesseract.min.js" integrity="sha512-QMGuBW4cKAKmxjxukfPlQqFL8Tc2yYWTBhg9o8fKx06BGZrNXMmafjtnmXthGasytcaIILHRrg5N5Hw0yOuSjw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>


    <script type="module">

    </script>
    <script>
        // 전역변수 txt 선언
        var txt;

        //  translatorArea 를 변수로 선언
        var $translatorArea = $("#translatorArea");

        // 로딩 이미지, 텍스트 영역
       // const $extractOn = $("#extractOn");
        //const $Loading = $("#Loading");


        // 여기는 너무 어려움ㅠ
        const recognize = async ({ target: { files }  }) =>
        {
            // 추출할 언어 선택
            const $langsel = $("#langsel").val();

            await $("#extractOn").hide();
            await $("#Loading").show();

            const
                { data:
                    { text }
                } = await Tesseract.recognize(
                    files[0],
                    $langsel,
                    {
                corePath: 'https://unpkg.com/tesseract.js-core@v2.0.0/tesseract-core.wasm.js',
                //logger: m => console.log(m),
            });
            //console.log(text);

            // 텍스트 추출이 끝나면 로딩 이미지 감추기
            $("#Loading").hide();

            // 텍스트 추출이 끝나면 텍스트 영역 보이도록 만들기
            $("#extractOn").show();

            // 추출된 내용을 extractTxt 에 넣어둠
            $("#extractTxt").val(text);
            txt = text;

        }

        // 페이지 로딩 시 실행되는 함수
        $(function (){
            // 처음에는 텍스트 창 안보이게 && 로딩창도 안보이게
            $("#extractOn").hide();
            $("#Loading").hide();

            const $img = $("#upload")

            // 이미지가 바뀔 때마다 recognize 함수가 실행됨 -> 사진에서 텍스트 가져오기
            $img.on("change", recognize);

            // 번역하기 버튼을 누르면 ajax 로 pagago 번역 실행
            $("#translator").on("click", function(){

                var source = $("#langsel").val();
                var target = $("#toTrans").val();

                var data = {
                    "text": txt,
                    "source": source,
                    "target": target
                }

                //console.log(data);

                $.ajax({
                    url : "/transword",
                    type : "GET",
                    data : data,
                    dataType : "text",
                    //contentType : "application/json;charset=utf-8",
                    success : function(result){
                        // console.log(result)
                        $("#transText").val(result);

                    }
                })
            })
        })

    </script>
    <style>
        #txt {
            border: 1px solid black;
            resize: none;
        }

        #transText {
            border: 1px solid black;
            resize: none;
            margin-left: 100px;
        }

    </style>
</head>
<body>
    <h1>번역 시작</h1>
    <div>
        1. 추출할 언어 선택<br>
        <select id="langsel">
            <option value="eng" selected> English </option>
            <option value="kor" selected> Korean </option>
            <option value="jpn" selected> Japanese </option>

        </select>
    </div>
    <br>
    <div>
        <div id="imgSelArea">
            <span>2. 글자를 인식할 파일선택</span><br>
            <input type="file" id="upload" name="upload">
        </div>
    </div>
    <br>
    <div id = translatorArea>

        <img src="/transLoading/loading.gif" id="Loading" alt="Loading">

        <div id="extractOn">
            <span>3. 번역할 언어 선택</span>
            <select id="toTrans">
                <option value='en' selected> English </option>
                <option value="ko" selected> Korean </option>
                <option value="ja" selected> Japanese </option>
            </select>
            <br>
            <textarea name="text" id="extractTxt" cols="100" rows="30" ></textarea>
            <textarea name="text" id="transText" cols="100" rows="30" ></textarea>
            <br>

            <input type="button" id="translator" name="translator" value="번역하기">
        </div>
   </div>
</body>
</html>

댓글