1. Introduction
프로세싱과 자바 Swing GUI 통합 - 002. 완전한 방식으로 한글 입출력이 가능한 텍스트필드 앱 만들기, 한영타자연습 프로그램 제작용 핵심 아이디어를 구현합니다.
이 예제는 프로세싱 고급 예제로 초보자용이 아닙니다. 제가 필요해서 올려둡니다. 이 예제는 Processing + Java 하이브리드(?) 앱 예제입니다. Processing 언어와 Java, Java GUI를 구현하는 Swing 등에 대한 충분한 사전 지식이 필요합니다.
이 프로그램은 프로세싱에서 자바 확장 기능을 이용하여 완전하게 한글 입력이 가능한 텍스트필드를 만드는 방법을 보여줍니다. 자바 코드가 대량 함유된(?) 사실상의 Java 앱이더라도 근본적으로는 Processing 프로그램입니다. 프로세싱 명령어들의 추가 확장이 100% 자유롭게 가능합니다.
프로세싱과 자바의 통합은 무난하나, ControlP5 자체 텍스트필드 객체와는 아직 완전하지 못합니다. ControlP5와의 연동은 잘 되지만, 한글 텍스트 직접 타이핑 입력 처리는 여전히 문제로 남아 있습니다. (간접 입력 방식은 해결됨.) 이 문제는 ControlP5 자체를 수정해야 할 듯하며, 이것은 이 앱의 제작 목표 범위를 벗어나는 일입니다. ControlP5의 내부 구조에 대해 아직 자세히 알지 못하기 때문에 그럴만한 역량이 되지 못합니다.
2. Code
// Update 1 코드임.
/*
Program Name : 프로세싱과 자바 통합 GUI - 002
Version : 1.0.2 Update 1
File Name : Java_TextField_Ex_009_.pde
Date : Aug. 19, 2025.
Licence : MIT, Not for Commercial Use Only.
Copyright : All rights are reserved. (C) James. 2025.
Notes :
* Processing + Java Swing JTextField Overlay + ControlP5 (v1.0.2 Update 1.)
* 기본적인 한영 텍스트 입출력 테스트 완료.
* 한글 표시 가능 텍스트필드 테스트 앱
* 3방향 실시간 동기화
* Swing IME 지원, CP5 UI와 연동.. ^.^;
* ControlP5 연동은 잘 되지만, 한글 텍스트 직접 입력 처리는 문제로 남음.
* CP5 자체를 수정해야. 이 앱의 제작 목표 범위를 넘어서는 일.
* 같은 Processing 창 안에 Swing 필드 오버레이
* 한글 IME 캐럿 항상 오른쪽 유지 오케이.
*/
import processing.awt.PSurfaceAWT;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import controlP5.*;
import processing.core.PFont;
PFont pfont;
ControlP5 cp5;
JTextField swingField;
JButton submitBtn;
JLabel hint;
JFrame frame;
PSurfaceAWT.SmoothCanvas canvas;
String sharedText = "";
void settings() {
size(720, 420); // JAVA2D 권장
}
void setup() {
// Processing용 한글 PFont 로드
try {
pfont = createFont("D2Coding_.ttf", 16, true);
} catch (Exception e) {
println("PFont 로드 실패: " + e.getMessage());
pfont = createFont("SansSerif", 16, true);
}
textFont(pfont);
textSize(16);
surface.setTitle("Processing + Java + ControlP5 (v1.0.2 Update 1.)");
surface.setResizable(true);
// ControlP5 구성
cp5 = new ControlP5(this);
cp5.addTextfield("cp5Field")
.setLabel("CP5 입력창")
.setPosition(20, 120)
.setSize(240, 28)
.setAutoClear(false)
.setFont(pfont); // for KR font.;
cp5.addButton("cp5Button")
.setLabel("Swing 필드에 값 쓰기")
.setPosition(270, 120)
.setSize(180, 28)
.setFont(pfont); // for KR font.
// pfont = createFont("D2Coding_.ttf", 16, true);
// Swing 오버레이 생성
SwingUtilities.invokeLater(() -> {
PSurfaceAWT awtSurface = (PSurfaceAWT) surface;
canvas = (PSurfaceAWT.SmoothCanvas) awtSurface.getNative();
frame = (JFrame) SwingUtilities.getWindowAncestor(canvas);
swingField = new JTextField();
swingField.setColumns(18);
swingField.setFont(new Font("Malgun Gothic", Font.PLAIN, 16));
submitBtn = new JButton("Submit(제출)");
submitBtn.setFont(new Font("Malgun Gothic", Font.PLAIN, 14));
hint = new JLabel("Swing <--> ControlP5 <--> 변수 실시간 동기화 (캐럿 유지)");
hint.setFont(new Font("Malgun Gothic", Font.PLAIN, 12));
hint.setForeground(new Color(0, 0, 0, 170));
// Swing --> CP5 + 캐럿 이동
DocumentListener swingListener = new DocumentListener() {
public void insertUpdate(DocumentEvent e) { updateFromSwing(); moveCaretToEnd(); }
public void removeUpdate(DocumentEvent e) { updateFromSwing(); moveCaretToEnd(); }
public void changedUpdate(DocumentEvent e) { updateFromSwing(); moveCaretToEnd(); }
};
swingField.getDocument().addDocumentListener(swingListener);
// (선택) IME 조합 중 캐럿 이동
swingField.addInputMethodListener(new InputMethodListener() {
public void inputMethodTextChanged(java.awt.event.InputMethodEvent e) { moveCaretToEnd(); }
public void caretPositionChanged(java.awt.event.InputMethodEvent e) { moveCaretToEnd(); }
});
submitBtn.addActionListener(e -> {
sharedText = swingField.getText();
updateCP5Field();
moveCaretToEnd();
});
// LayeredPane에 추가
JLayeredPane lp = frame.getLayeredPane();
lp.add(swingField, JLayeredPane.PALETTE_LAYER);
lp.add(submitBtn, JLayeredPane.PALETTE_LAYER);
lp.add(hint, JLayeredPane.PALETTE_LAYER);
layoutOverlay();
// 창 이동/리사이즈 대응
frame.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) { layoutOverlay(); }
public void componentMoved(ComponentEvent e) { layoutOverlay(); }
});
});
}
void draw() {
background(245);
fill(20);
textSize(16);
text("Processing Draw 영역", 24, 20);
fill(0, 120, 180);
textSize(14);
text("공유 변수(sharedText): " + sharedText, 26, 40);
}
// ===== 동기화 로직 =====
void updateFromSwing() {
sharedText = swingField.getText();
updateCP5Field();
}
void updateFromCP5(String newVal) {
sharedText = newVal;
SwingUtilities.invokeLater(() -> {
if (!swingField.getText().equals(newVal)) {
swingField.setText(newVal);
moveCaretToEnd();
}
});
}
void updateCP5Field() {
if (!cp5.get(Textfield.class, "cp5Field").getText().equals(sharedText)) {
cp5.get(Textfield.class, "cp5Field").setText(sharedText);
}
}
// CP5 이벤트
public void cp5Field(String val) {
updateFromCP5(val);
}
public void cp5Button() {
updateFromCP5("CP5에서 보낸 값");
}
// ===== 캐럿 항상 끝으로 =====
void moveCaretToEnd() {
SwingUtilities.invokeLater(() -> {
if (swingField != null) {
int len = swingField.getText().length();
swingField.setCaretPosition(len);
}
});
}
// ===== Swing 오버레이 위치 지정 =====
void layoutOverlay() {
Insets ins = frame.getInsets();
int x0 = ins.left + 20;
int y0 = ins.top + 20;
int tfW = 240;
int tfH = 28;
swingField.setBounds(x0, y0, tfW, tfH);
submitBtn.setBounds(x0 + tfW + 8, y0, 150, tfH);
hint.setBounds(x0, y0 + tfH + 6, 400, 20);
}
// 종료시 정리
void dispose() {
SwingUtilities.invokeLater(() -> {
if (frame != null) {
JLayeredPane lp = frame.getLayeredPane();
lp.remove(swingField);
lp.remove(submitBtn);
lp.remove(hint);
lp.revalidate();
lp.repaint();
}
});
}
3. Result

4. Notes
너무 길어질 것 같아서 간결하게 핵심만 정리합니다. 기억력의 한계로 노트 대신 보려는 용도입니다.
■ 요약
1) 하나의 Processing 창 안에 Swing 컴포넌트가 ControlP5 UI와 함께 표시
2) 창 크기 변경·이동 시 오버레이 위치 자동 조정
3) 한글 입력 시 캐럿이 항상 오른쪽 끝에 고정 (IME 조합 중간 이동까지 반영)
4) 3방향 동기화: Swing <--> CP5 <--> 내부 변수
■ 코드의 전체 목적
A. Processing 스케치 내부에서
1) Swing의 JTextField,
2) ControlP5의 Textfield 두 입력 필드를 동시에 사용하고,
B. 3방향 동기화 :
1) Swing JTextField <--> ControlP5 Textfield
2) 두 필드 <--> 내부 공유 변수(sharedText)
C. 한글 IME 포함 모든 입력에서 캐럿(커서)을 항상 오른쪽 끝에 유지
D. 모든 UI를 같은 Processing 창 안에 겹쳐서 표시
■ 주요 구성 요소
A. Swing 컴포넌트 (오버레이)
1) JTextField swingField : 메인 Swing 입력창, 한글 IME 안정 지원
2) JButton submitBtn : Swing에서 입력된 내용을 확정해 CP5 쪽에 반영하는 버튼
3) JLabel hint : 도움말/상태 표시용 라벨
4) JLayeredPane을 이용해 Processing 캔버스 위에 직접 배치
B. ControlP5 컴포넌트
1) cp5Field : 스케치 안쪽에 위치하는 ControlP5의 텍스트필드
2) cp5Button : Swing 입력창 값을 변경하기 위해 ControlP5 쪽에서 누르는 버튼
C. 내부 공유 변수
1) sharedText : 두 필드가 동시에 참조·갱신하는 문자열
--> Swing과 CP5가 항상 같은 값을 보도록 하는 데이터 허브 역할
■ 동기화 로직
A. Swing --> 공유 변수 --> CP5
1) DocumentListener에서 Swing 필드 변경을 감지
2) updateFromSwing() 호출 --> sharedText 갱신 --> updateCP5Field()로 CP5 필드 업데이트
3) 캐럿 위치를 끝으로 이동 (moveCaretToEnd())
B. CP5 --> 공유 변수 --> Swing
1) ControlP5 cp5Field(String val) 이벤트에서 updateFromCP5(val) 호출
2) Swing 필드 텍스트 갱신 후 캐럿을 끝으로 이동
C. 버튼 액션
1) Swing submitBtn 클릭 : Swing 필드 내용 --> 공유 변수 --> CP5 반영
2) CP5 버튼 클릭 : 미리 지정한 값 --> 공유 변수 --> Swing 필드 반영
■ 캐럿(커서) 항상 오른쪽 끝 유지
A. 구현 방법
1) DocumentListener 이벤트(insertUpdate, removeUpdate, changedUpdate)마다 moveCaretToEnd() 호출
2) CP5 --> Swing 갱신 시 moveCaretToEnd() 호출
3) InputMethodListener 추가
--> IME 조합 중간에도 캐럿 이동 시도 (필요 시 주석 가능)
B. 함수
1) 주요 코드
void moveCaretToEnd() {
SwingUtilities.invokeLater(() -> {
if (swingField != null) {
int len = swingField.getText().length();
swingField.setCaretPosition(len);
}
});
}
2) invokeLater를 쓰는 이유 :
- 입력 이벤트 처리가 끝난 다음 시점에 캐럿을 이동시켜, IME 조합 끊김을 방지
■ Swing 오버레이 배치
1) Processing의 native AWT Canvas를 가져와 해당 캔버스의 부모 JFrame과 JLayeredPane를 참조
2) setBounds()로 원하는 픽셀 좌표에 직접 배치
3) frame.getInsets()로 타이틀바/테두리 영역 보정
4) ComponentListener로 창 이동·리사이즈 시 자동 재배치
■ 동작 흐름 요약
A. Processing 실행 --> setup()
1) ControlP5 UI 생성
2) Swing UI를 같은 창의 LayeredPane에 추가
B. 사용자 Swing 필드 입력
1) 입력 감지(DocumentListener) --> 공유변수·CP5 업데이트 --> 캐럿 끝 이동
C. 사용자 CP5 필드 입력
1) 이벤트 함수(cp5Field)에서 Swing 필드 갱신 --> 캐럿 끝 이동
D. Swing 제출 버튼 / CP5 버튼 클릭
1) 공유변수 값을 반대쪽에 전달 --> 동기화 완성
■ 기타
1) IME 완전 대응 : Swing 필드 덕에 한글/중문/일문 등 조합형 문자 입력 안정
2) 레이어드 UI : 별도 창 없이 하나의 Processing 프레임에서 Swing+CP5 혼합 가능
3) 명확한 상태 공유 : sharedText 하나로 두 UI가 항상 일치
4) 위치 동기화 : 창 이동·크기 변경에도 UI가 깨지지 않음
■ 기타 응용 연습 과제
1) 타자 연습 프로그램 ...
2) 외부 파일 로딩 ...
5. Files
6. Ref.
1) 황기태, [ 명품 Java Programming, 5E. ] 생능. 2024.
2) Casey Reas, Ben Fry 공저, [ Processing : A Programming Handbook, 2E. ] MIT. 2014.
3) 비슷한 주제, 이전 참고용 코드 : https://grammar.tistory.com/71
Happy Programming!
^.^;
'Programming > Processing' 카테고리의 다른 글
| [ ProceJava ] 한글 입력 가능 텍스트필드 앱 만들기 - 1 (0) | 2025.08.17 |
|---|---|
| Interfascia 한글 입력 - 1 (0) | 2025.08.16 |
| Hello Processing - 001 (0) | 2025.02.09 |
| Using Korean Font with the GUI Lib. for Processing v4.3 (0) | 2024.12.09 |
| Processing v.4.3 x64 정식 버전 발표됨. 이미 예전에! ^.^; (0) | 2023.07.12 |