feat: 프로필 사진 — 구글 아바타 동기화 + 사용자 지정 이미지

- member.google_picture(구글 아바타 URL), profile_image(사용자 지정 data URL) 컬럼 추가(멱등)
- 구글 로그인 시 picture 클레임 추출 → 신규 저장, 기존은 변경 시 동기화
- PUT /api/auth/profile-image: 사용자 지정 사진 설정/해제(빈값=해제→구글 폴백), data URL·용량 검증
- MemberResponse 에 googlePicture/profileImage 노출(표시 우선순위 프론트 처리)
- 관리자 목록(findAll)은 무거운 profile_image 제외해 가볍게 조회

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-28 11:39:35 +09:00
parent 6256d3e63d
commit 98f5f51112
10 changed files with 89 additions and 6 deletions
+5
View File
@@ -30,6 +30,11 @@ UPDATE member SET google_id = provider_id WHERE provider = 'GOOGLE' AND google_i
-- 멤버십 플랜 (무료/유료). 기존 회원은 FREE 로 시작 (멱등).
ALTER TABLE member ADD COLUMN IF NOT EXISTS plan VARCHAR(20) NOT NULL DEFAULT 'FREE' COMMENT 'FREE / PREMIUM' AFTER role;
-- 프로필 사진. google_picture 는 구글 로그인 시 동기화되는 구글 아바타 URL,
-- profile_image 는 사용자가 직접 올린 커스텀 이미지(data URL). 표시 우선순위: profile_image > google_picture > 이니셜.
ALTER TABLE member ADD COLUMN IF NOT EXISTS google_picture VARCHAR(500) NULL COMMENT '구글 아바타 URL (로그인 시 동기화)' AFTER google_id;
ALTER TABLE member ADD COLUMN IF NOT EXISTS profile_image MEDIUMTEXT NULL COMMENT '사용자 지정 프로필 이미지(data URL)' AFTER google_picture;
-- ============================================================
-- 앱 전역 설정 (단일 행, id=1). 관리자 화면에서 변경.
-- ============================================================
+16 -4
View File
@@ -12,6 +12,8 @@
<result property="provider" column="provider"/>
<result property="providerId" column="provider_id"/>
<result property="googleId" column="google_id"/>
<result property="googlePicture" column="google_picture"/>
<result property="profileImage" column="profile_image"/>
<result property="role" column="role"/>
<result property="plan" column="plan"/>
<result property="status" column="status"/>
@@ -20,15 +22,17 @@
</resultMap>
<sql id="columns">
id, login_id, password, name, email, provider, provider_id, google_id, role, plan, status, created_at, updated_at
id, login_id, password, name, email, provider, provider_id, google_id, google_picture, profile_image, role, plan, status, created_at, updated_at
</sql>
<select id="findById" parameterType="long" resultMap="memberResultMap">
SELECT <include refid="columns"/> FROM member WHERE id = #{id}
</select>
<!-- 관리자 목록: 무거운 profile_image(MEDIUMTEXT)·password 는 제외하고 가볍게 조회 -->
<select id="findAll" resultMap="memberResultMap">
SELECT <include refid="columns"/> FROM member ORDER BY id
SELECT id, login_id, name, email, provider, provider_id, google_id, role, plan, status, created_at, updated_at
FROM member ORDER BY id
</select>
<select id="findByLoginId" parameterType="string" resultMap="memberResultMap">
@@ -66,9 +70,9 @@
<insert id="insert" parameterType="com.sb.web.auth.domain.Member"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO member (login_id, password, name, email, provider, provider_id, google_id, role, status, created_at, updated_at)
INSERT INTO member (login_id, password, name, email, provider, provider_id, google_id, google_picture, role, status, created_at, updated_at)
VALUES (#{loginId}, #{password}, #{name}, #{email},
#{provider}, #{providerId}, #{googleId}, #{role}, #{status}, NOW(), NOW())
#{provider}, #{providerId}, #{googleId}, #{googlePicture}, #{role}, #{status}, NOW(), NOW())
</insert>
<update id="updatePassword">
@@ -79,6 +83,14 @@
UPDATE member SET name = #{name}, email = #{email}, updated_at = NOW() WHERE id = #{id}
</update>
<update id="updateGooglePicture">
UPDATE member SET google_picture = #{googlePicture}, updated_at = NOW() WHERE id = #{id}
</update>
<update id="updateProfileImage">
UPDATE member SET profile_image = #{profileImage}, updated_at = NOW() WHERE id = #{id}
</update>
<update id="updateRole">
UPDATE member SET role = #{role}, updated_at = NOW() WHERE id = #{id}
</update>