Difference between pg_column_size(table.*) and pg_column_size(table.col1) + pg_column_size (table.col2)
The 24 byte/row difference rings a bell: it is the same size as the row header in tables and indexes.  What I did not realize up until your question is that the same header size applies apparently to every row you return in a query:

SELECT pg_column_size((1::integer, 2::smallint));
 pg_column_size 
────────────────
             30


but

SELECT pg_column_size(1::integer) + pg_column_size(1::smallint) AS pg_column_size;
 pg_column_size 
────────────────
              6


When passing a row to pg_column_size(), it calculates the size of the whole row, including the header.

Now playing a bit further with this, there are some interesting facts you can find. 

For example, one can check the difference caused by padding the columns up to the nearest 4 bytes (where k is a positive integer):

SELECT pg_column_size((1::smallint, 2::integer));
 pg_column_size 
────────────────
             32


Here we got two more bytes than in the first example, the remaining 2 bytes after the smallint got 'wasted'.  This may be important when choosing the order of columns in huge tables - for example, we can squeeze something into that 'empty' space:

SELECT pg_column_size((1::smallint, FALSE, FALSE, 2::integer));
 pg_column_size 
────────────────
             32


The other important thing is that columns that have NULL as a value have zero size here:

SELECT pg_column_size((NULL::smallint, 2::integer));
 pg_column_size 
────────────────
             28


In contrast, you'll get NULL as a result when trying to add up the column individually:

SELECT pg_column_size(NULL::smallint) +  pg_column_size(2::integer) AS pg_column_size;
 pg_column_size 
────────────────
           NULL