작업 상태

#springboot 버전 변경 후에 발생함

//id 'org.springframework.boot' version '3.0.2'
id 'org.springframework.boot' version '3.1.3'

 

에러 메시지 

expression.EvaluationException: Accessing member 'requestUri' is forbidden for type 'class org.springframework.web.servlet.support.RequestContext' in this expression context.

원인

문제부분

    <script th:inline="javascript">
        /*<![CDATA[*/

        //let loc = /*[[${#ctx.springRequestContext.requestUri}]]*/'';

 

#부트 버전 변경후 나머지 종속성으로 thymleaf버전도 3.1.3으로 변경되었는데 3.1에서는 제거된 방식이라고 함

 

 

처리

 

기존 방식

 

controller

    @GetMapping(value = "/admin/note.html")
    public String indexGetMethod(Model model) {
        return "admin/note";
    };

 

view

    <script th:inline="javascript">
        /*<![CDATA[*/

        //let loc = /*[[${#ctx.springRequestContext.requestUri}]]*/'';
 

 

 

변경후 > 자바쪽에서 현 위치를 파악후 전달해줌

 

controller

    @GetMapping(value = "/admin/note.html")
    public String indexGetMethod(Model model, HttpServletRequest request) {
        model.addAttribute("servletPath", request.getServletPath());
        return "admin/note";
    };

 

view

    <script th:inline="javascript">
        /*<![CDATA[*/

        //let loc = /*[[${servletPath}]]*/'';


참고

getRequestURI is null with Netty and Spring Boot 3 - Stack Overflow

이 번 글에서는 마지막으로 이 카테고리의 주제인 "에지브라우저에서 chatGPT 탭으로 메시지 전송후 대화" 부분 까지 진행해 보도록 하겠습니다.

이 전 글에서 기본 javascript를 사용해서 크롬의 메시지 송수신을 하는 부분 까지 진행했습니다. 이번 글에서는 

- Content Script (콘텐트 스크립트), - Popup (팝업): 에서 써드파티 라이브러리 사용법에 대해서 추가로 알아보고 바로 탭에서 수신한 메시지를 기존 new Chat Dom에 추가 하고 질문을 수행하는 부분까지 진행하도록 하겠습니다.

 

예제코드 : tistory_openapi/edgeGpt4 at master · a3040/tistory_openapi · GitHub 

 

- Content Script (콘텐트 스크립트), - Popup (팝업): 에서 써드파티 라이브러리 사용법이 각각 다릅니다.

 

- Popup (팝업)에서는 popup.html에서 직접 참조해서 사용하는 방식입니다.

<script src="./scripts/jquery.min.js"></script>
<script src="./scripts/axios.min.js"></script>

- Content Script (콘텐트 스크립트)는 manifest 설정을 통해 자동 주입해주는 형태입니다.

- 그게 아니면 dom 제어를 통해서 직접 추가 해주는 방식도 사용가능 합니다.

아래 예제 코드 설명에서 설명을 조금더 하겠습니다.

 

아래 이미지는 예제 코드를 실행한 상태입니다.

브라우저에서 메시지 전송 전

1.popup.html에 기본 주제를 적어둘 inputbox를 추가했습니다.

2. input box에 원하는 주제를 입력하고 "메시지전송" 버튼을 클릭합니다.

 

아래 이미지는 메시지 전송 버튼을 클릭한 이후 이미지 입니다.

gpt와 의 연결

1. "메시지전송" 버튼을 클릭으로 수신한 메시지 정보입니다.

2. 수신 메시지를 gpt DOM으로 넘겨 주는 메시지 입니다.

3. gpt DOM의 대화 상자로 popup에서 작성한 메시지가 전송된 상태입니다.

4. gpt DOM에 메시지를 전송후 openai에게 전송하는 부분입니다. 보통 엔터나 종이비행기 버튼을 클릭하는 액션을 수행하는 부분입니다.

 

예제코드 설명

manifest.json 설정파일 입니다.

{
    "name": "Hello4",
    "version": "0.0.1",
    "manifest_version": 3,
    "description": "Hello",
    "action": {
        "default_popup": "popup.html"
    },
    "permissions": [
        "debugger",
        "tabs",
        "scripting"
    ],
    "content_scripts": [
        {
            "matches": [
                "https://chat.openai.com/*"
            ],
            "js": [
                "scripts/jquery.min.js",
                "content.js"
            ]
        }
    ]
}

- 권한 부분과 콘텐츠 스크립트에서 사용할 "content_scripts"의 js 부분에 jquery.min.js를 추가했습니다. 추가가 되면 확장 프로그램이 해당 스크립트를 matches탭에 추가 해줍니다. 순서를 jquery 추가 하고 content.js를 추가해서 content.js에서 jquery를 사용할수 있습니다.

 

popup.html  popup.js에서 jquery를 사용하기 위해 직접 불러오고 있습니다.

<html>
<meta charset="utf-8" />

<body style="width:200px;">
    hello
    <input type="text" id="msg" value="" />
    <button type="button" id="sendMsg" style="width:50px">메시지전송</button>
    <script src="./scripts/jquery.min.js"></script>
    <script src="./scripts/popup.js"></script>
</body>

</html>

popup.js 기존 코드에서 jquery를 사용하는 방식으로 바뀌었습니다. jquery사용 예시용으로 변경했습니다.

console.log(`팝업 js 불림, 폴더구조확인용 scripts/popup.js`);

function send_to_content(msg){
    chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
        console.log("현재탭에 붙어있는 content에 msg보냄");
        const response = chrome.tabs.sendMessage(
            tabs[0].id,
            {
                from: 'popup',
                to: 'content',
                msg: msg,
                // topics: extactTopics
            },
            function (response) {
                console.log(response);
                window.close();
            }
        );
    })
}

$(document).ready(function () {  //jquery사용
    $("#sendMsg").on("click", function () {
        console.log("clicked");
        const msg = $("#msg").val();
        send_to_content(msg);
    });
});

content.js 마지막 파일입니다. 직접 chatGPT DOM과 연동되는 부분입니다. 역시 jquery 예시를 위해 jquery로 부분 변환했습니다.

메시지를 수신하고 수신한 메시지를 chatGPT DOM의 대화 창에 추가 한후 키보드 이벤트를 발생시켜 submit하는 코드입니다.

jquery 예제 추가후 아래의 예시 코드는 주석을 해제해도 동작하지 않습니다. 원인을 확인 해본 결과 jquery와 충돌하는 듯 합니다. 수정방법은 manifest에서 jquery 제거, 관련 jquery 제거 후 gpt_textarea.value 값 설정, dispatchEvent를 발생 시키면 정상적으로 동작하게 됩니다.

console.log(`js 불림, 폴더구조확인용 content.js`);

chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.from === 'popup') {
        try {
            console.log('메시지 수신1:', message);
            sendResponse('메시지를 수신했습니다.');
            bridge(message.msg);
        }catch(error){
            console.error(error);
        }
    }
});

function bridge(msg){

    const gpt_textarea = document.querySelector('form textarea');            
    const gpt_button = document.querySelector('form button[class*="bottom"]')
    gpt_button.disabled = true;
    gpt_textarea.value = msg; 
    console.log(`수신한 msg->gpt에게 넘김:${msg}`);
    /*
    // 새로운 키보드 이벤트 생성
    const event = new KeyboardEvent('keydown', {
        key: 'Enter',
        keyCode: 13,
        code: 'Enter',
        which: 13,
        keyIdentifier: 'Enter',
        view: window,
        bubbles: true,
        cancelable: true,
    });
    
    // 이벤트를 원하는 요소로 보내기 
    gpt_textarea.dispatchEvent(event); 
    */
    console.log(`주석 해제후 gpt_textarea에 엔터 키보드 이벤트전송시 질문이 openai로 전송됨`);
}

function insertDiv(){
    var div = document.createElement("div");
    div.style.height = "100px";
    div.style.backgroundColor = "blue";
  
    var body = document.body;
    var lastChild = body.lastElementChild;
  
    // body 요소의 맨 마지막에 div 요소를 추가합니다.
    body.insertBefore(div, lastChild.nextSibling);
}

function jinsertDiv(){
    var div = $("<div></div>").css({
        "height": "100px",
        "background-color": "red"
    });
    
    var lastChild = $("body").children().last();
    
    // body 요소의 맨 마지막에 div 요소를 추가합니다.
    lastChild.after(div);
}

jinsertDiv();
// insertDiv();

스크린샷 다 만든 후에 엔터키 이벤트가 충돌나는것을 발견 했습니다. 필요하신 분이 생기면 다음 글 작성하면서 예제코드를 하나 더 만들겠습니다. 지금은 동작하는 예시 하나 스크린샷하고 이번 카테고리 글은 마무리하겠습니다.

동일한 구조로 동작하는 확장프로그램입니다. jquery는 사용하지 않았습니다. 확장아이콘 클릭시 값이 초기화 되어 localStorage에 저정하고 불러오는 구조로 작업했습니다.

이상으로 에지브라우저에서 chatGPT 탭으로 메시지 전송후 대화에 대한 글을 마치겠습니다.

 

참고

이런걸 쓰면 content.js 작업할때 gpt dom사용이 편할것 같습니다. 

chatgptjs/chatgpt.js: 🤖 A powerful client-side JavaScript library for ChatGPT (github.com)

 

GitHub - chatgptjs/chatgpt.js: 🤖 A powerful client-side JavaScript library for ChatGPT

🤖 A powerful client-side JavaScript library for ChatGPT - GitHub - chatgptjs/chatgpt.js: 🤖 A powerful client-side JavaScript library for ChatGPT

github.com

 

에지확장프로그램에서 사용자가 특정탭의 content제어 스크립트로 메시지를 전송하고 응답하는 방법을 알아보도록 하겠습니다. 프로그램 구조는 

 

- Popup (팝업):쪽에서 "메시지 전송"버튼을 클릭하면 특정 메시지를 content제어 스크립트로 전송합니다.

- Content Script (콘텐트 스크립트): 팝업쪽에서 전송한 메시지를 받아 처리하고 수신에 대한 응답을 전송합니다.

 

예제코드 : tistory_openapi/edgeGpt3 at master · a3040/tistory_openapi (github.com)

 

아래의 그림은 예제코드 실행 후 popup과 content쪽 두곳의 개발도구를 열어둔 상태입니다. 

ㅓ확장프로그램에서 메시지 보내기전

이미지의 번호 내용은

1번은 팝업창쪽 개발도구 창입니다.

2번은 탭쪽에 삽입된 content.js에서 보여지는 개발도구 창입니다. 현재 둘다 로딩완료 로그가 보입니다.

3번 "메시지전송" 버튼을 클릭하면 현재 열린탭에 메시지를 전송합니다.

 

윗쪽 대기 상태 이미지에서 "메시지전송" 버튼을 클릭해서 메시지를 전송하고 응답을 받은 결과 이미지 입니다. 페이지의 파란색 부분은 content.js로 직접 탭안의 DOM을 제어해서 UI작성을 할수 있다는 예시용입니다.

확장프로그램에서 메시지 전송 후

이미지의 번호 내용은

1 "메시지전송" 버튼을 클릭

2 예제코드에 있는 버튼 클릭에 대한 로깅 정보입니다.

3 메시지 전송 전에 대상이 전송 대상이 되는 탭을 발견 후 메시지 입니다.

4 content쪽 수신 로그입니다. 

5.content쪽에서 수신후 보낸 응답 처리 로그 메시지 입니다.

 

주요 사용 함수는

  1. chrome.tabs.query: Chrome 창 또는 탭을 쿼리하여 일치하는 탭 정보를 반환합니다.
  2. chrome.tabs.sendMessage: 탭으로 메시지를 보냅니다. 
  3. chrome.runtime.onMessage: 이벤트를 통해 메시지를 수신하는데 사용됩니다.

 chrome.tabs 관련 함수는 popup쪽에서 브라우저 탭을 확인 하고 특정탭에 메시지를 보내기 위해 사용됩니다.

  • 이 대화하기에서는 chatGPT가 열려있는 현재 탭을 선택하기 때문에 현재 창의 탭정보를 얻는 아래의 방법을 사용합니다.
  • chrome.tabs.query({ active: true, currentWindow: true })

chrome.runtime.onMessage는 content쪽에서 메시지를 수신하기 위해 사용됩니다.

 

예제 코드 설명입니다.

- 설정파일 : manifest.json 특이사항없습니다.

 

popup.html : 메시지버튼 추가하고 약간 크기 조절했습니다.

<html>
    <meta charset="utf-8" />
    <body style="width:200px;">
        hello
        <button type="button" id="sendMsg" style="width:50px">메시지전송</button>
        <script src="./scripts/popup.js"></script>
    </body>
</html>

popup.js : send_to_content 함수는 위에서 설명한것과 같이 chrome.tabs.query를 통해 현재 활성화된 열린 창의 탭을 조회하고 반환합니다.  탭이 조회되면 callback을통해 조회된 tabs를 전달받고 chrome.tabs.sendMessage(  tabId: number,  message: any, ..) 를 통해 tab으로 메시지를 전송합니다. 

 

그 아래 코드는 popup.html의 sendMsg버튼에 클릭 이벤트를 추가하는 부분입니다. 클릭시 send_to_content함수를 통해 메시지를 전달합니다.

console.log(`팝업 js 불림, 폴더구조확인용 scripts/popup.js`);

function send_to_content(msg) {
    chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
        console.log("현재탭에 붙어있는 content에 msg보냄");
        const response = chrome.tabs.sendMessage(
            tabs[0].id,
            {
                from: 'popup',
                to: 'content',
                msg: msg,
                // topics: extactTopics
            },
            function (response) {
                console.log(response);
                // window.close();
            }
        );
    })
}

const sendMsg = document.getElementById("sendMsg");
if (sendMsg) {
    sendMsg.onclick = function () {
        console.log("clicked");
        send_to_content(`전송하기`);
    };
}

content.js : 

스크립트가 로드될때 chrome.runtime.onMessage에 이벤트 처리 함수를 추가 합니다. 그리고 탭주소가  https://chat.openai.com/*인 경우 페이지 가장 하단에 높이가 100인 div를 삽입합니다. 예제에서는 사용자와 대화창 위치를 popup.html에 넣을 예정이지만 content.js에 대화창을 두게될 경우 예시입니다.

console.log(`js 불림, 폴더구조확인용 content.js`);

chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.from === 'popup') {
        try {
            console.log('메시지 수신1:', message);
            sendResponse('메시지를 수신했습니다.');
        }catch(error){
            console.error(error);
        }
    }
});

function insertDiv(){
    var div = document.createElement("div");
    div.style.height = "100px";
    div.style.backgroundColor = "blue";
  
    var body = document.body;
    var lastChild = body.lastElementChild;
  
    // body 요소의 맨 마지막에 div 요소를 추가합니다.
    body.insertBefore(div, lastChild.nextSibling);
}
insertDiv();

이번글에서는 브라우저와 특정 페이지의 contents에 메시지를 전송하고 처리하는 예제를 보았습니다. 다음에는 jquery를 추가해서 content, popup쪽에서 사용하는 방법과 popup에서 작성되어 온 메시지를 기존 gpt 대화창에 입력하는 부분을 진행해 보도록 하겠습니다.

 

참고

https://developer.chrome.com/docs/extensions/reference/tabs/

 

chrome.tabs - Chrome Developers

Build the next generation of web experiences.

developer.chrome.com

https://developer.chrome.com/docs/extensions/reference/runtime/#event-onMessage

 

chrome.runtime - Chrome Developers

Build the next generation of web experiences.

developer.chrome.com

 

 

 

에지확장프로그램은 브라우저에서 동작하면서 각각의 탭, 그리고 탭안에 열린 페이지의 내용(content)을 우리가 원하는대로 제어할수 있도록 해줍니다. 그리고 탭들이 각각 독립적으로 동작하고 있어서 그 탭들간의 공유내용과 통신을 background라는 부분을 통해서 수행할수 있도록 합니다. 그리고 탭의 content에서 탭간 공유를 위한 background 파일에 대한 설정을 manifest.json 파일에 작성하게 됩니다.

 

현재 글의 주제인 chatGPT와 대화에서는 탭하나의 content를 제어하게 되어서, background는 사용을 안하게 될것같습니다.그리고 탭의 주소가 https://chat.openai.com 인 경우,그 탭의 html및 dom을 제어하게될 content.js 마지막으로 사용자의 문자를 받아서 탭에 붙어 있는 content로 문자열을 보내게 될 popup.html, popup.js파일로 구성됩니다.

 

코드참조 : tistory_openapi/edgeGpt2 at master · a3040/tistory_openapi (github.com)  

 

이전글에서 수행한것과 같은 방법으로 확장프로그램을추가 합니다.

chatGPT와 대화하기 위한 에지확장프로그램 소개 및 기본 구조 (tistory.com)

 

동작 화면은 이전 페이지의 위 화면과 동일합니다. 단 manifest.json에 content제어 파일과 대상이 될 탭 정보 추가, 탭에서 탭안의 DOM을 제어할 content.js, 그리고 사용자와 대화할 popup.html 수정 및 popup.js 추가가 있습니다. 각각의 파일들의 주요 역할과 내용은 전 글에서 이야기했듯 아래와 같습니다.

 

에지확장프로그램의 구성
- Manifest 파일: 
에지 확장 프로그램의 설정과 정보를 담는 JSON 형식의 파일입니다.
프로그램 이름, 버전, 아이콘, 권한 등의 정보를 정의합니다.

- Background Script (백그라운드 스크립트):
에지 확장 프로그램의 백그라운드에서 실행되는 JavaScript 코드입니다.
백그라운드에서 실행되는 주기적인 작업, 이벤트 처리 등을 담당합니다.

- Content Script (콘텐트 스크립트):
웹 페이지의 콘텐츠와 상호작용하기 위해 삽입되는 JavaScript 코드입니다.
웹 페이지의 DOM 요소에 접근하고 조작할 수 있습니다.

- Popup (팝업):
사용자 인터페이스를 제공하는 팝업 창입니다.
사용자의 입력을 받고 처리하는 등의 작업을 수행합니다.

 

예제 코드에 대한 간단한 설명 및 디버그 로그를 확인하는 방법을 알아보도록 하겠습니다.

{
    "name": "Hello2",
    "version": "0.0.1",
    "manifest_version": 3,
    "description": "Hello",
    "action": {
        "default_popup": "popup.html"
    },
    "content_scripts": [
        {
            "matches": [
                "https://chat.openai.com/*"
            ],
            "js": [
                "content.js"
            ]
        }
    ]
}

    "content_scripts": [   부분이 추가 되었습니다. 여러 탭들 중 https://chat.openai.com/* 인 탭에 대해서 우리가 만든 content.js라는 파일을 삽입(injection)합니다.

 

popup.html

<html>
    <meta charset="utf-8" />
    hello
    <script src="./scripts/popup.js"></script>
</html>

scripts/popup.js .. 폴더 구조 확인을 위해 scripts 폴더를 생성했습니다 그리고 디버그 로그 확인을 위해 간단한 로깅을 추가 했습니다.

console.log(`팝업 js 불림, 폴더구조확인용 scripts/popup.js`);

사용자와의 인터페이스용 popup.html의 개발자 콘손을 "확장프로그램 아이콘 클릭" > 팝업된 창에서 마우스 오른쪽 클릭 > "검사" 메뉴를 클릭 하면 아래의 이미지와 같이 새창이 뜨면서 popup.html의 내용과 실행된 popup.js내용을 볼수 있습니다.

확장프로그램 &gt; popup.html 개발자도구열기

content.js   디버그 로그 확인을 위해 간단한 로깅을 추가 했습니다.

console.log(`js 불림, 폴더구조확인용 content.js`);

content.js 파일을 manifest에서 matchs 값과 탭의 주소창이 맞는 탭에 injection되어 있습니다. 따라서 지금 설정과 같은 경우 주소창의 주소가  https://chat.openai.com/*  인 탭에서 F12로 개발자 창을 열 경우 아래와 같이 실행된 content.js의 로그를 볼수 있습니다. 아래 그림의 "확장프로그램 아이콘 부분의 빨간 박스 표시"는 윗쪽 popup.html 개발자 도구 창을 열기 전의 모습입니다.

확장프로그램 &gt; content 개발자도구열기

 

마지막으로 이번 예시에서는 사용하지 않았지만 다른 확장프로그램 개발중 background를 사용하기 위해  manifest.json에 background 제어 설정을 한경우 아래의 그림과 같이 "확장관리" 에 보시면 "서비스 작업자"라는 링크가 활성화되어 있고 이 링크를 클릭하면 background가 실행되고 있는 부분의 개발자 도구가 열리게 됩니다. 우리가 만든 Hello2는 bacground 제어 설정이 없어서 "서비스 작업자" 링크가 없습니다.

확장프로그램 &gt; background 개발자도구열기

 

에지확장프로그램에서는 위에서 설명한 기본 세 부분이 서로 통신하며 탭과 탭의 DOM을 제어하게 됩니다. 

- Background Script (백그라운드 스크립트):
- Content Script (콘텐트 스크립트):
- Popup (팝업):

그리고 세부분은 각각의 개발자 도구를 갖고 있습니다.

이번 글에서는 에지확장프로그램의 간단한 구조와 각각 구조별 개발자 모드를 확인하는 방법에 대해서 이야기 했습니다.

다음 글에서는 popup.html에서 사용자에게 문자열을 받고 특정 주소의 탭에 전달하는 부분을 해보도록 하겠습니다.

 

참고:

Extension concepts and architecture - Microsoft Edge Development | Microsoft Learn

 

Extension concepts and architecture - Microsoft Edge Development

The architecture of Microsoft Edge extensions, and core concepts to build extensions.

learn.microsoft.com

 

+ Recent posts